mirror of
https://github.com/2dust/v2rayN.git
synced 2025-10-13 20:09:12 +00:00
Compare commits
34 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
a1490d0ac1 | ||
![]() |
b23f49ffce | ||
![]() |
9a9e28e494 | ||
![]() |
65ee5eb510 | ||
![]() |
1f42d32e1a | ||
![]() |
f2ed8c1d6b | ||
![]() |
308b216d1b | ||
![]() |
c713f5c8f5 | ||
![]() |
6771eb25d1 | ||
![]() |
91af50f99a | ||
![]() |
a559586e71 | ||
![]() |
929520775d | ||
![]() |
4eaf31bbf8 | ||
![]() |
1607525539 | ||
![]() |
31b5b4ca0c | ||
![]() |
64c7fea2bc | ||
![]() |
f76fd364a2 | ||
![]() |
0a1d6db9d1 | ||
![]() |
7a750a127e | ||
![]() |
fce4a7b74c | ||
![]() |
fec7353703 | ||
![]() |
40c90d5b3b | ||
![]() |
9c58fec8d4 | ||
![]() |
11343a30fd | ||
![]() |
3693a7fee6 | ||
![]() |
a452bbe140 | ||
![]() |
185c5e4bfb | ||
![]() |
bbe64aa970 | ||
![]() |
513662d89a | ||
![]() |
22f0d04f01 | ||
![]() |
d7c5161431 | ||
![]() |
12cc09d0c9 | ||
![]() |
5b12c36da5 | ||
![]() |
e970372a9f |
93 changed files with 4276 additions and 1382 deletions
|
@ -1,7 +1,7 @@
|
||||||
<Project>
|
<Project>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Version>7.15.0</Version>
|
<Version>7.15.2</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
|
|
@ -6,23 +6,23 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageVersion Include="Avalonia.AvaloniaEdit" Version="11.3.0" />
|
<PackageVersion Include="Avalonia.AvaloniaEdit" Version="11.3.0" />
|
||||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.6" />
|
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.7" />
|
||||||
<PackageVersion Include="Avalonia.Desktop" Version="11.3.6" />
|
<PackageVersion Include="Avalonia.Desktop" Version="11.3.7" />
|
||||||
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.6" />
|
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.7" />
|
||||||
<PackageVersion Include="Avalonia.ReactiveUI" Version="11.3.6" />
|
<PackageVersion Include="Avalonia.ReactiveUI" Version="11.3.7" />
|
||||||
<PackageVersion Include="CliWrap" Version="3.9.0" />
|
<PackageVersion Include="CliWrap" Version="3.9.0" />
|
||||||
<PackageVersion Include="Downloader" Version="4.0.3" />
|
<PackageVersion Include="Downloader" Version="4.0.3" />
|
||||||
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.3.0" />
|
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.3.1" />
|
||||||
<PackageVersion Include="MaterialDesignThemes" Version="5.2.1" />
|
<PackageVersion Include="MaterialDesignThemes" Version="5.3.0" />
|
||||||
<PackageVersion Include="MessageBox.Avalonia" Version="3.2.0" />
|
<PackageVersion Include="MessageBox.Avalonia" Version="3.2.0" />
|
||||||
<PackageVersion Include="QRCoder" Version="1.6.0" />
|
<PackageVersion Include="QRCoder" Version="1.7.0" />
|
||||||
<PackageVersion Include="ReactiveUI" Version="20.4.1" />
|
<PackageVersion Include="ReactiveUI" Version="20.4.1" />
|
||||||
<PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" />
|
<PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" />
|
||||||
<PackageVersion Include="ReactiveUI.WPF" Version="20.4.1" />
|
<PackageVersion Include="ReactiveUI.WPF" Version="20.4.1" />
|
||||||
<PackageVersion Include="Semi.Avalonia" Version="11.2.1.10" />
|
<PackageVersion Include="Semi.Avalonia" Version="11.3.7" />
|
||||||
<PackageVersion Include="Semi.Avalonia.AvaloniaEdit" Version="11.2.0.1" />
|
<PackageVersion Include="Semi.Avalonia.AvaloniaEdit" Version="11.2.0.1" />
|
||||||
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.2.1.10" />
|
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.3.7" />
|
||||||
<PackageVersion Include="NLog" Version="6.0.4" />
|
<PackageVersion Include="NLog" Version="6.0.5" />
|
||||||
<PackageVersion Include="sqlite-net-pcl" Version="1.9.172" />
|
<PackageVersion Include="sqlite-net-pcl" Version="1.9.172" />
|
||||||
<PackageVersion Include="TaskScheduler" Version="2.12.2" />
|
<PackageVersion Include="TaskScheduler" Version="2.12.2" />
|
||||||
<PackageVersion Include="WebDav.Client" Version="2.9.0" />
|
<PackageVersion Include="WebDav.Client" Version="2.9.0" />
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 270f023fcca4ef9149480c575d502815efd83531
|
Subproject commit ffb2850df0991495d0918e13cc5701737f26175a
|
|
@ -84,4 +84,14 @@ public static class Extension
|
||||||
{
|
{
|
||||||
return source.Concat(new[] { string.Empty }).ToList();
|
return source.Concat(new[] { string.Empty }).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool IsGroupType(this EConfigType configType)
|
||||||
|
{
|
||||||
|
return configType is EConfigType.PolicyGroup or EConfigType.ProxyChain;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsComplexType(this EConfigType configType)
|
||||||
|
{
|
||||||
|
return configType is EConfigType.Custom or EConfigType.PolicyGroup or EConfigType.ProxyChain;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,116 +67,4 @@ public static class ProcUtils
|
||||||
Logging.SaveLog(_tag, ex);
|
Logging.SaveLog(_tag, ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task ProcessKill(int pid)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await ProcessKill(Process.GetProcessById(pid), false);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.SaveLog(_tag, ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task ProcessKill(Process? proc, bool review)
|
|
||||||
{
|
|
||||||
if (proc is null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
GetProcessKeyInfo(proc, review, out var procId, out var fileName, out var processName);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (Utils.IsNonWindows())
|
|
||||||
{
|
|
||||||
proc?.Kill(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.SaveLog(_tag, ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
proc?.Kill();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.SaveLog(_tag, ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
proc?.Close();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.SaveLog(_tag, ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
proc?.Dispose();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.SaveLog(_tag, ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
await Task.Delay(300);
|
|
||||||
await ProcessKillByKeyInfo(review, procId, fileName, processName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void GetProcessKeyInfo(Process? proc, bool review, out int? procId, out string? fileName, out string? processName)
|
|
||||||
{
|
|
||||||
procId = null;
|
|
||||||
fileName = null;
|
|
||||||
processName = null;
|
|
||||||
if (!review)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
procId = proc?.Id;
|
|
||||||
fileName = proc?.MainModule?.FileName;
|
|
||||||
processName = proc?.ProcessName;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.SaveLog(_tag, ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task ProcessKillByKeyInfo(bool review, int? procId, string? fileName, string? processName)
|
|
||||||
{
|
|
||||||
if (review && procId != null && fileName != null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var lstProc = Process.GetProcessesByName(processName);
|
|
||||||
foreach (var proc2 in lstProc)
|
|
||||||
{
|
|
||||||
if (proc2.Id == procId)
|
|
||||||
{
|
|
||||||
Logging.SaveLog($"{_tag}, KillProcess not completing the job, procId");
|
|
||||||
await ProcessKill(proc2, false);
|
|
||||||
}
|
|
||||||
if (proc2.MainModule != null && proc2.MainModule?.FileName == fileName)
|
|
||||||
{
|
|
||||||
Logging.SaveLog($"{_tag}, KillProcess not completing the job, fileName");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.SaveLog(_tag, ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,13 +85,19 @@ public class Utils
|
||||||
/// Base64 Encode
|
/// Base64 Encode
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="plainText"></param>
|
/// <param name="plainText"></param>
|
||||||
|
/// <param name="removePadding"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static string Base64Encode(string plainText)
|
public static string Base64Encode(string plainText, bool removePadding = false)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
|
var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
|
||||||
return Convert.ToBase64String(plainTextBytes);
|
var base64 = Convert.ToBase64String(plainTextBytes);
|
||||||
|
if (removePadding)
|
||||||
|
{
|
||||||
|
base64 = base64.TrimEnd('=');
|
||||||
|
}
|
||||||
|
return base64;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -112,7 +118,7 @@ public class Utils
|
||||||
{
|
{
|
||||||
if (plainText.IsNullOrEmpty())
|
if (plainText.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
return "";
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
plainText = plainText.Trim()
|
plainText = plainText.Trim()
|
||||||
|
@ -947,7 +953,7 @@ public class Utils
|
||||||
if (SetUnixFileMode(fileName))
|
if (SetUnixFileMode(fileName))
|
||||||
{
|
{
|
||||||
Logging.SaveLog($"Successfully set the file execution permission, {fileName}");
|
Logging.SaveLog($"Successfully set the file execution permission, {fileName}");
|
||||||
return "";
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fileName.Contains(' '))
|
if (fileName.Contains(' '))
|
||||||
|
|
|
@ -7,11 +7,11 @@ namespace ServiceLib.Common;
|
||||||
* http://stackoverflow.com/questions/6266820/working-example-of-createjobobject-setinformationjobobject-pinvoke-in-net
|
* http://stackoverflow.com/questions/6266820/working-example-of-createjobobject-setinformationjobobject-pinvoke-in-net
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public sealed class Job : IDisposable
|
public sealed class WindowsJob : IDisposable
|
||||||
{
|
{
|
||||||
private IntPtr handle = IntPtr.Zero;
|
private IntPtr handle = IntPtr.Zero;
|
||||||
|
|
||||||
public Job()
|
public WindowsJob()
|
||||||
{
|
{
|
||||||
handle = CreateJobObject(IntPtr.Zero, null);
|
handle = CreateJobObject(IntPtr.Zero, null);
|
||||||
var extendedInfoPtr = IntPtr.Zero;
|
var extendedInfoPtr = IntPtr.Zero;
|
||||||
|
@ -94,7 +94,7 @@ namespace ServiceLib.Common;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
~Job()
|
~WindowsJob()
|
||||||
{
|
{
|
||||||
Dispose(false);
|
Dispose(false);
|
||||||
}
|
}
|
|
@ -12,5 +12,7 @@ public enum EConfigType
|
||||||
TUIC = 8,
|
TUIC = 8,
|
||||||
WireGuard = 9,
|
WireGuard = 9,
|
||||||
HTTP = 10,
|
HTTP = 10,
|
||||||
Anytls = 11
|
Anytls = 11,
|
||||||
|
PolicyGroup = 101,
|
||||||
|
ProxyChain = 102,
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,9 @@ namespace ServiceLib.Enums;
|
||||||
|
|
||||||
public enum EMultipleLoad
|
public enum EMultipleLoad
|
||||||
{
|
{
|
||||||
|
LeastPing,
|
||||||
|
Fallback,
|
||||||
Random,
|
Random,
|
||||||
RoundRobin,
|
RoundRobin,
|
||||||
LeastPing,
|
|
||||||
LeastLoad
|
LeastLoad
|
||||||
}
|
}
|
||||||
|
|
8
v2rayN/ServiceLib/Enums/ERuleType.cs
Normal file
8
v2rayN/ServiceLib/Enums/ERuleType.cs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
namespace ServiceLib.Enums;
|
||||||
|
|
||||||
|
public enum ERuleType
|
||||||
|
{
|
||||||
|
ALL = 0,
|
||||||
|
Routing = 1,
|
||||||
|
DNS = 2,
|
||||||
|
}
|
|
@ -23,6 +23,7 @@ public enum EViewAction
|
||||||
RoutingRuleDetailsWindow,
|
RoutingRuleDetailsWindow,
|
||||||
AddServerWindow,
|
AddServerWindow,
|
||||||
AddServer2Window,
|
AddServer2Window,
|
||||||
|
AddGroupServerWindow,
|
||||||
DNSSettingWindow,
|
DNSSettingWindow,
|
||||||
RoutingSettingWindow,
|
RoutingSettingWindow,
|
||||||
OptionSettingWindow,
|
OptionSettingWindow,
|
||||||
|
|
|
@ -50,6 +50,7 @@ public class Global
|
||||||
public const string DirectTag = "direct";
|
public const string DirectTag = "direct";
|
||||||
public const string BlockTag = "block";
|
public const string BlockTag = "block";
|
||||||
public const string DnsTag = "dns-module";
|
public const string DnsTag = "dns-module";
|
||||||
|
public const string BalancerTagSuffix = "-round";
|
||||||
public const string StreamSecurity = "tls";
|
public const string StreamSecurity = "tls";
|
||||||
public const string StreamSecurityReality = "reality";
|
public const string StreamSecurityReality = "reality";
|
||||||
public const string Loopback = "127.0.0.1";
|
public const string Loopback = "127.0.0.1";
|
||||||
|
@ -83,8 +84,7 @@ public class Global
|
||||||
|
|
||||||
public const string SingboxDirectDNSTag = "direct_dns";
|
public const string SingboxDirectDNSTag = "direct_dns";
|
||||||
public const string SingboxRemoteDNSTag = "remote_dns";
|
public const string SingboxRemoteDNSTag = "remote_dns";
|
||||||
public const string SingboxOutboundResolverTag = "outbound_resolver";
|
public const string SingboxLocalDNSTag = "local_local";
|
||||||
public const string SingboxFinalResolverTag = "final_resolver";
|
|
||||||
public const string SingboxHostsDNSTag = "hosts_dns";
|
public const string SingboxHostsDNSTag = "hosts_dns";
|
||||||
public const string SingboxFakeDNSTag = "fake_dns";
|
public const string SingboxFakeDNSTag = "fake_dns";
|
||||||
|
|
||||||
|
@ -315,6 +315,8 @@ public class Global
|
||||||
EConfigType.HTTP,
|
EConfigType.HTTP,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public static readonly HashSet<EConfigType> SingboxOnlyConfigType = SingboxSupportConfigType.Except(XraySupportConfigType).ToHashSet();
|
||||||
|
|
||||||
public static readonly List<string> DomainStrategies =
|
public static readonly List<string> DomainStrategies =
|
||||||
[
|
[
|
||||||
AsIs,
|
AsIs,
|
||||||
|
|
|
@ -357,6 +357,11 @@ public static class ConfigHandler
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (profileItem.ConfigType.IsGroupType())
|
||||||
|
{
|
||||||
|
var profileGroupItem = await AppManager.Instance.GetProfileGroupItem(it.IndexId);
|
||||||
|
await AddGroupServerCommon(config, profileItem, profileGroupItem, true);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await AddServerCommon(config, profileItem, true);
|
await AddServerCommon(config, profileItem, true);
|
||||||
|
@ -1074,6 +1079,37 @@ public static class ConfigHandler
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task<int> AddGroupServerCommon(Config config, ProfileItem profileItem, ProfileGroupItem profileGroupItem, bool toFile = true)
|
||||||
|
{
|
||||||
|
var maxSort = -1;
|
||||||
|
if (profileItem.IndexId.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
profileItem.IndexId = Utils.GetGuid(false);
|
||||||
|
maxSort = ProfileExManager.Instance.GetMaxSort();
|
||||||
|
}
|
||||||
|
var groupType = profileItem.ConfigType == EConfigType.ProxyChain ? EConfigType.ProxyChain.ToString() : profileGroupItem.MultipleLoad.ToString();
|
||||||
|
profileItem.Address = $"{profileItem.CoreType}-{groupType}";
|
||||||
|
if (maxSort > 0)
|
||||||
|
{
|
||||||
|
ProfileExManager.Instance.SetSort(profileItem.IndexId, maxSort + 1);
|
||||||
|
}
|
||||||
|
if (toFile)
|
||||||
|
{
|
||||||
|
await SQLiteHelper.Instance.ReplaceAsync(profileItem);
|
||||||
|
if (profileGroupItem != null)
|
||||||
|
{
|
||||||
|
profileGroupItem.IndexId = profileItem.IndexId;
|
||||||
|
await ProfileGroupItemManager.Instance.SaveItemAsync(profileGroupItem);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ProfileGroupItemManager.Instance.GetOrCreateAndMarkDirty(profileItem.IndexId);
|
||||||
|
await ProfileGroupItemManager.Instance.SaveTo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Compare two profile items to determine if they represent the same server
|
/// Compare two profile items to determine if they represent the same server
|
||||||
/// Used for deduplication and server matching
|
/// Used for deduplication and server matching
|
||||||
|
@ -1145,7 +1181,7 @@ public static class ConfigHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a custom server that combines multiple servers for load balancing
|
/// Create a group server that combines multiple servers for load balancing
|
||||||
/// Generates a configuration file that references multiple servers
|
/// Generates a configuration file that references multiple servers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="config">Current configuration</param>
|
/// <param name="config">Current configuration</param>
|
||||||
|
@ -1153,45 +1189,54 @@ public static class ConfigHandler
|
||||||
/// <param name="coreType">Core type to use (Xray or sing_box)</param>
|
/// <param name="coreType">Core type to use (Xray or sing_box)</param>
|
||||||
/// <param name="multipleLoad">Load balancing algorithm</param>
|
/// <param name="multipleLoad">Load balancing algorithm</param>
|
||||||
/// <returns>Result object with success state and data</returns>
|
/// <returns>Result object with success state and data</returns>
|
||||||
public static async Task<RetResult> AddCustomServer4Multiple(Config config, List<ProfileItem> selecteds, ECoreType coreType, EMultipleLoad multipleLoad)
|
public static async Task<RetResult> AddGroupServer4Multiple(Config config, List<ProfileItem> selecteds, ECoreType coreType, EMultipleLoad multipleLoad, string? subId)
|
||||||
{
|
{
|
||||||
var indexId = Utils.GetMd5(Global.CoreMultipleLoadConfigFileName);
|
var result = new RetResult();
|
||||||
var configPath = Utils.GetConfigPath(Global.CoreMultipleLoadConfigFileName);
|
|
||||||
|
|
||||||
var result = await CoreConfigHandler.GenerateClientMultipleLoadConfig(config, configPath, selecteds, coreType, multipleLoad);
|
var indexId = Utils.GetGuid(false);
|
||||||
if (result.Success != true)
|
var childProfileIndexId = Utils.List2String(selecteds.Select(p => p.IndexId).ToList());
|
||||||
{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!File.Exists(configPath))
|
var remark = subId.IsNullOrEmpty() ? string.Empty : $"{(await AppManager.Instance.GetSubItem(subId)).Remarks} ";
|
||||||
{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
var profileItem = await AppManager.Instance.GetProfileItem(indexId) ?? new();
|
|
||||||
profileItem.IndexId = indexId;
|
|
||||||
if (coreType == ECoreType.Xray)
|
if (coreType == ECoreType.Xray)
|
||||||
{
|
{
|
||||||
profileItem.Remarks = multipleLoad switch
|
remark += multipleLoad switch
|
||||||
{
|
{
|
||||||
EMultipleLoad.Random => ResUI.menuSetDefaultMultipleServerXrayRandom,
|
EMultipleLoad.LeastPing => ResUI.menuGenGroupMultipleServerXrayLeastPing,
|
||||||
EMultipleLoad.RoundRobin => ResUI.menuSetDefaultMultipleServerXrayRoundRobin,
|
EMultipleLoad.Fallback => ResUI.menuGenGroupMultipleServerXrayFallback,
|
||||||
EMultipleLoad.LeastPing => ResUI.menuSetDefaultMultipleServerXrayLeastPing,
|
EMultipleLoad.Random => ResUI.menuGenGroupMultipleServerXrayRandom,
|
||||||
EMultipleLoad.LeastLoad => ResUI.menuSetDefaultMultipleServerXrayLeastLoad,
|
EMultipleLoad.RoundRobin => ResUI.menuGenGroupMultipleServerXrayRoundRobin,
|
||||||
_ => ResUI.menuSetDefaultMultipleServerXrayRoundRobin,
|
EMultipleLoad.LeastLoad => ResUI.menuGenGroupMultipleServerXrayLeastLoad,
|
||||||
|
_ => ResUI.menuGenGroupMultipleServerXrayRoundRobin,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if (coreType == ECoreType.sing_box)
|
else if (coreType == ECoreType.sing_box)
|
||||||
{
|
{
|
||||||
profileItem.Remarks = ResUI.menuSetDefaultMultipleServerSingBoxLeastPing;
|
remark += multipleLoad switch
|
||||||
|
{
|
||||||
|
EMultipleLoad.LeastPing => ResUI.menuGenGroupMultipleServerSingBoxLeastPing,
|
||||||
|
EMultipleLoad.Fallback => ResUI.menuGenGroupMultipleServerSingBoxFallback,
|
||||||
|
_ => ResUI.menuGenGroupMultipleServerSingBoxLeastPing,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
profileItem.Address = Global.CoreMultipleLoadConfigFileName;
|
var profile = new ProfileItem
|
||||||
profileItem.ConfigType = EConfigType.Custom;
|
{
|
||||||
profileItem.CoreType = coreType;
|
IndexId = indexId,
|
||||||
|
CoreType = coreType,
|
||||||
await AddServerCommon(config, profileItem, true);
|
ConfigType = EConfigType.PolicyGroup,
|
||||||
|
Remarks = remark,
|
||||||
|
};
|
||||||
|
if (!subId.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
profile.Subid = subId;
|
||||||
|
}
|
||||||
|
var profileGroup = new ProfileGroupItem
|
||||||
|
{
|
||||||
|
ChildItems = childProfileIndexId,
|
||||||
|
MultipleLoad = multipleLoad,
|
||||||
|
IndexId = indexId,
|
||||||
|
};
|
||||||
|
var ret = await AddGroupServerCommon(config, profile, profileGroup, true);
|
||||||
|
result.Success = ret == 0;
|
||||||
result.Data = indexId;
|
result.Data = indexId;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -1209,12 +1254,21 @@ public static class ConfigHandler
|
||||||
ProfileItem? itemSocks = null;
|
ProfileItem? itemSocks = null;
|
||||||
if (node.ConfigType != EConfigType.Custom && coreType != ECoreType.sing_box && config.TunModeItem.EnableTun)
|
if (node.ConfigType != EConfigType.Custom && coreType != ECoreType.sing_box && config.TunModeItem.EnableTun)
|
||||||
{
|
{
|
||||||
|
var tun2SocksAddress = node.Address;
|
||||||
|
if (node.ConfigType.IsGroupType())
|
||||||
|
{
|
||||||
|
var lstAddresses = (await ProfileGroupItemManager.GetAllChildDomainAddresses(node.IndexId)).ToList();
|
||||||
|
if (lstAddresses.Count > 0)
|
||||||
|
{
|
||||||
|
tun2SocksAddress = Utils.List2String(lstAddresses);
|
||||||
|
}
|
||||||
|
}
|
||||||
itemSocks = new ProfileItem()
|
itemSocks = new ProfileItem()
|
||||||
{
|
{
|
||||||
CoreType = ECoreType.sing_box,
|
CoreType = ECoreType.sing_box,
|
||||||
ConfigType = EConfigType.SOCKS,
|
ConfigType = EConfigType.SOCKS,
|
||||||
Address = Global.Loopback,
|
Address = Global.Loopback,
|
||||||
SpiderX = node.Address, // Tun2SocksAddress
|
SpiderX = tun2SocksAddress, // Tun2SocksAddress
|
||||||
Port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks)
|
Port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1430,16 +1484,7 @@ public static class ConfigHandler
|
||||||
if (profileItem is null)
|
if (profileItem is null)
|
||||||
{
|
{
|
||||||
profileItem = Hysteria2Fmt.ResolveFull2(strData, subRemarks);
|
profileItem = Hysteria2Fmt.ResolveFull2(strData, subRemarks);
|
||||||
}
|
}
|
||||||
if (profileItem is null)
|
|
||||||
{
|
|
||||||
profileItem = Hysteria2Fmt.ResolveFull(strData, subRemarks);
|
|
||||||
}
|
|
||||||
//Is naiveproxy configuration
|
|
||||||
if (profileItem is null)
|
|
||||||
{
|
|
||||||
profileItem = NaiveproxyFmt.ResolveFull(strData, subRemarks);
|
|
||||||
}
|
|
||||||
if (profileItem is null || profileItem.Address.IsNullOrEmpty())
|
if (profileItem is null || profileItem.Address.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -2229,8 +2274,6 @@ public static class ConfigHandler
|
||||||
BlockBindingQuery = true,
|
BlockBindingQuery = true,
|
||||||
DirectDNS = Global.DomainDirectDNSAddress.FirstOrDefault(),
|
DirectDNS = Global.DomainDirectDNSAddress.FirstOrDefault(),
|
||||||
RemoteDNS = Global.DomainRemoteDNSAddress.FirstOrDefault(),
|
RemoteDNS = Global.DomainRemoteDNSAddress.FirstOrDefault(),
|
||||||
SingboxOutboundsResolveDNS = Global.DomainDirectDNSAddress.FirstOrDefault(),
|
|
||||||
SingboxFinalResolveDNS = Global.DomainPureIPDNSAddress.FirstOrDefault()
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -132,24 +132,4 @@ public static class CoreConfigHandler
|
||||||
await File.WriteAllTextAsync(fileName, result.Data.ToString());
|
await File.WriteAllTextAsync(fileName, result.Data.ToString());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<RetResult> GenerateClientMultipleLoadConfig(Config config, string fileName, List<ProfileItem> selecteds, ECoreType coreType, EMultipleLoad multipleLoad)
|
|
||||||
{
|
|
||||||
var result = new RetResult();
|
|
||||||
if (coreType == ECoreType.sing_box)
|
|
||||||
{
|
|
||||||
result = await new CoreConfigSingboxService(config).GenerateClientMultipleLoadConfig(selecteds);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result = await new CoreConfigV2rayService(config).GenerateClientMultipleLoadConfig(selecteds, multipleLoad);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.Success != true)
|
|
||||||
{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
await File.WriteAllTextAsync(fileName, result.Data.ToString());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -155,61 +155,60 @@ public class BaseFmt
|
||||||
|
|
||||||
protected static int ResolveStdTransport(NameValueCollection query, ref ProfileItem item)
|
protected static int ResolveStdTransport(NameValueCollection query, ref ProfileItem item)
|
||||||
{
|
{
|
||||||
item.Flow = query["flow"] ?? "";
|
item.Flow = GetQueryValue(query, "flow");
|
||||||
item.StreamSecurity = query["security"] ?? "";
|
item.StreamSecurity = GetQueryValue(query, "security");
|
||||||
item.Sni = query["sni"] ?? "";
|
item.Sni = GetQueryValue(query, "sni");
|
||||||
item.Alpn = Utils.UrlDecode(query["alpn"] ?? "");
|
item.Alpn = GetQueryDecoded(query, "alpn");
|
||||||
item.Fingerprint = Utils.UrlDecode(query["fp"] ?? "");
|
item.Fingerprint = GetQueryDecoded(query, "fp");
|
||||||
item.PublicKey = Utils.UrlDecode(query["pbk"] ?? "");
|
item.PublicKey = GetQueryDecoded(query, "pbk");
|
||||||
item.ShortId = Utils.UrlDecode(query["sid"] ?? "");
|
item.ShortId = GetQueryDecoded(query, "sid");
|
||||||
item.SpiderX = Utils.UrlDecode(query["spx"] ?? "");
|
item.SpiderX = GetQueryDecoded(query, "spx");
|
||||||
item.Mldsa65Verify = Utils.UrlDecode(query["pqv"] ?? "");
|
item.Mldsa65Verify = GetQueryDecoded(query, "pqv");
|
||||||
item.AllowInsecure = (query["allowInsecure"] ?? "") == "1" ? "true" : "";
|
item.AllowInsecure = new[] { "allowInsecure", "allow_insecure", "insecure" }.Any(k => (query[k] ?? "") == "1") ? "true" : "";
|
||||||
|
|
||||||
item.Network = query["type"] ?? nameof(ETransport.tcp);
|
item.Network = GetQueryValue(query, "type", nameof(ETransport.tcp));
|
||||||
switch (item.Network)
|
switch (item.Network)
|
||||||
{
|
{
|
||||||
case nameof(ETransport.tcp):
|
case nameof(ETransport.tcp):
|
||||||
item.HeaderType = query["headerType"] ?? Global.None;
|
item.HeaderType = GetQueryValue(query, "headerType", Global.None);
|
||||||
item.RequestHost = Utils.UrlDecode(query["host"] ?? "");
|
item.RequestHost = GetQueryDecoded(query, "host");
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case nameof(ETransport.kcp):
|
case nameof(ETransport.kcp):
|
||||||
item.HeaderType = query["headerType"] ?? Global.None;
|
item.HeaderType = GetQueryValue(query, "headerType", Global.None);
|
||||||
item.Path = Utils.UrlDecode(query["seed"] ?? "");
|
item.Path = GetQueryDecoded(query, "seed");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case nameof(ETransport.ws):
|
case nameof(ETransport.ws):
|
||||||
case nameof(ETransport.httpupgrade):
|
case nameof(ETransport.httpupgrade):
|
||||||
item.RequestHost = Utils.UrlDecode(query["host"] ?? "");
|
item.RequestHost = GetQueryDecoded(query, "host");
|
||||||
item.Path = Utils.UrlDecode(query["path"] ?? "/");
|
item.Path = GetQueryDecoded(query, "path", "/");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case nameof(ETransport.xhttp):
|
case nameof(ETransport.xhttp):
|
||||||
item.RequestHost = Utils.UrlDecode(query["host"] ?? "");
|
item.RequestHost = GetQueryDecoded(query, "host");
|
||||||
item.Path = Utils.UrlDecode(query["path"] ?? "/");
|
item.Path = GetQueryDecoded(query, "path", "/");
|
||||||
item.HeaderType = Utils.UrlDecode(query["mode"] ?? "");
|
item.HeaderType = GetQueryDecoded(query, "mode");
|
||||||
item.Extra = Utils.UrlDecode(query["extra"] ?? "");
|
item.Extra = GetQueryDecoded(query, "extra");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case nameof(ETransport.http):
|
case nameof(ETransport.http):
|
||||||
case nameof(ETransport.h2):
|
case nameof(ETransport.h2):
|
||||||
item.Network = nameof(ETransport.h2);
|
item.Network = nameof(ETransport.h2);
|
||||||
item.RequestHost = Utils.UrlDecode(query["host"] ?? "");
|
item.RequestHost = GetQueryDecoded(query, "host");
|
||||||
item.Path = Utils.UrlDecode(query["path"] ?? "/");
|
item.Path = GetQueryDecoded(query, "path", "/");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case nameof(ETransport.quic):
|
case nameof(ETransport.quic):
|
||||||
item.HeaderType = query["headerType"] ?? Global.None;
|
item.HeaderType = GetQueryValue(query, "headerType", Global.None);
|
||||||
item.RequestHost = query["quicSecurity"] ?? Global.None;
|
item.RequestHost = GetQueryValue(query, "quicSecurity", Global.None);
|
||||||
item.Path = Utils.UrlDecode(query["key"] ?? "");
|
item.Path = GetQueryDecoded(query, "key");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case nameof(ETransport.grpc):
|
case nameof(ETransport.grpc):
|
||||||
item.RequestHost = Utils.UrlDecode(query["authority"] ?? "");
|
item.RequestHost = GetQueryDecoded(query, "authority");
|
||||||
item.Path = Utils.UrlDecode(query["serviceName"] ?? "");
|
item.Path = GetQueryDecoded(query, "serviceName");
|
||||||
item.HeaderType = Utils.UrlDecode(query["mode"] ?? Global.GrpcGunMode);
|
item.HeaderType = GetQueryDecoded(query, "mode", Global.GrpcGunMode);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -239,4 +238,14 @@ public class BaseFmt
|
||||||
var url = $"{Utils.UrlEncode(userInfo)}@{GetIpv6(address)}:{port}";
|
var url = $"{Utils.UrlEncode(userInfo)}@{GetIpv6(address)}:{port}";
|
||||||
return $"{Global.ProtocolShares[eConfigType]}{url}{query}{remark}";
|
return $"{Global.ProtocolShares[eConfigType]}{url}{query}{remark}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected static string GetQueryValue(NameValueCollection query, string key, string defaultValue = "")
|
||||||
|
{
|
||||||
|
return query[key] ?? defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static string GetQueryDecoded(NameValueCollection query, string key, string defaultValue = "")
|
||||||
|
{
|
||||||
|
return Utils.UrlDecode(GetQueryValue(query, key, defaultValue));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ public class ClashFmt : BaseFmt
|
||||||
{
|
{
|
||||||
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
|
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
|
||||||
{
|
{
|
||||||
if (Contains(strData, "external-controller", "-port", "proxies"))
|
if (Contains(strData, "rules", "-port", "proxies"))
|
||||||
{
|
{
|
||||||
var fileName = WriteAllText(strData, "yaml");
|
var fileName = WriteAllText(strData, "yaml");
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ public class FmtHandler
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logging.SaveLog(_tag, ex);
|
Logging.SaveLog(_tag, ex);
|
||||||
return "";
|
return string.Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,10 +21,10 @@ public class Hysteria2Fmt : BaseFmt
|
||||||
|
|
||||||
var query = Utils.ParseQueryString(url.Query);
|
var query = Utils.ParseQueryString(url.Query);
|
||||||
ResolveStdTransport(query, ref item);
|
ResolveStdTransport(query, ref item);
|
||||||
item.Path = Utils.UrlDecode(query["obfs-password"] ?? "");
|
item.Path = GetQueryDecoded(query, "obfs-password");
|
||||||
item.AllowInsecure = (query["insecure"] ?? "") == "1" ? "true" : "false";
|
item.AllowInsecure = GetQueryValue(query, "insecure") == "1" ? "true" : "false";
|
||||||
|
|
||||||
item.Ports = Utils.UrlDecode(query["mport"] ?? "");
|
item.Ports = GetQueryDecoded(query, "mport");
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
@ -63,24 +63,6 @@ public class Hysteria2Fmt : BaseFmt
|
||||||
return ToUri(EConfigType.Hysteria2, item.Address, item.Port, item.Id, dicQuery, remark);
|
return ToUri(EConfigType.Hysteria2, item.Address, item.Port, item.Id, dicQuery, remark);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
|
|
||||||
{
|
|
||||||
if (Contains(strData, "server", "up", "down", "listen", "<html>", "<body>"))
|
|
||||||
{
|
|
||||||
var fileName = WriteAllText(strData);
|
|
||||||
|
|
||||||
var profileItem = new ProfileItem
|
|
||||||
{
|
|
||||||
CoreType = ECoreType.hysteria,
|
|
||||||
Address = fileName,
|
|
||||||
Remarks = subRemarks ?? "hysteria_custom"
|
|
||||||
};
|
|
||||||
return profileItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ProfileItem? ResolveFull2(string strData, string? subRemarks)
|
public static ProfileItem? ResolveFull2(string strData, string? subRemarks)
|
||||||
{
|
{
|
||||||
if (Contains(strData, "server", "auth", "up", "down", "listen"))
|
if (Contains(strData, "server", "auth", "up", "down", "listen"))
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
namespace ServiceLib.Handler.Fmt;
|
|
||||||
|
|
||||||
public class NaiveproxyFmt : BaseFmt
|
|
||||||
{
|
|
||||||
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
|
|
||||||
{
|
|
||||||
if (Contains(strData, "listen", "proxy", "<html>", "<body>"))
|
|
||||||
{
|
|
||||||
var fileName = WriteAllText(strData);
|
|
||||||
|
|
||||||
var profileItem = new ProfileItem
|
|
||||||
{
|
|
||||||
CoreType = ECoreType.naiveproxy,
|
|
||||||
Address = fileName,
|
|
||||||
Remarks = subRemarks ?? "naiveproxy_custom"
|
|
||||||
};
|
|
||||||
return profileItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -42,7 +42,7 @@ public class ShadowsocksFmt : BaseFmt
|
||||||
// item.port);
|
// item.port);
|
||||||
//url = Utile.Base64Encode(url);
|
//url = Utile.Base64Encode(url);
|
||||||
//new Sip002
|
//new Sip002
|
||||||
var pw = Utils.Base64Encode($"{item.Security}:{item.Id}");
|
var pw = Utils.Base64Encode($"{item.Security}:{item.Id}", true);
|
||||||
return ToUri(EConfigType.Shadowsocks, item.Address, item.Port, pw, null, remark);
|
return ToUri(EConfigType.Shadowsocks, item.Address, item.Port, pw, null, remark);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ public class SocksFmt : BaseFmt
|
||||||
remark = "#" + Utils.UrlEncode(item.Remarks);
|
remark = "#" + Utils.UrlEncode(item.Remarks);
|
||||||
}
|
}
|
||||||
//new
|
//new
|
||||||
var pw = Utils.Base64Encode($"{item.Security}:{item.Id}");
|
var pw = Utils.Base64Encode($"{item.Security}:{item.Id}", true);
|
||||||
return ToUri(EConfigType.SOCKS, item.Address, item.Port, pw, null, remark);
|
return ToUri(EConfigType.SOCKS, item.Address, item.Port, pw, null, remark);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ public class TuicFmt : BaseFmt
|
||||||
|
|
||||||
var query = Utils.ParseQueryString(url.Query);
|
var query = Utils.ParseQueryString(url.Query);
|
||||||
ResolveStdTransport(query, ref item);
|
ResolveStdTransport(query, ref item);
|
||||||
item.HeaderType = query["congestion_control"] ?? "";
|
item.HeaderType = GetQueryValue(query, "congestion_control");
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,8 +24,8 @@ public class VLESSFmt : BaseFmt
|
||||||
item.Id = Utils.UrlDecode(url.UserInfo);
|
item.Id = Utils.UrlDecode(url.UserInfo);
|
||||||
|
|
||||||
var query = Utils.ParseQueryString(url.Query);
|
var query = Utils.ParseQueryString(url.Query);
|
||||||
item.Security = query["encryption"] ?? Global.None;
|
item.Security = GetQueryValue(query, "encryption", Global.None);
|
||||||
item.StreamSecurity = query["security"] ?? "";
|
item.StreamSecurity = GetQueryValue(query, "security");
|
||||||
_ = ResolveStdTransport(query, ref item);
|
_ = ResolveStdTransport(query, ref item);
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
|
|
|
@ -24,10 +24,10 @@ public class WireguardFmt : BaseFmt
|
||||||
|
|
||||||
var query = Utils.ParseQueryString(url.Query);
|
var query = Utils.ParseQueryString(url.Query);
|
||||||
|
|
||||||
item.PublicKey = Utils.UrlDecode(query["publickey"] ?? "");
|
item.PublicKey = GetQueryDecoded(query, "publickey");
|
||||||
item.Path = Utils.UrlDecode(query["reserved"] ?? "");
|
item.Path = GetQueryDecoded(query, "reserved");
|
||||||
item.RequestHost = Utils.UrlDecode(query["address"] ?? "");
|
item.RequestHost = GetQueryDecoded(query, "address");
|
||||||
item.ShortId = Utils.UrlDecode(query["mtu"] ?? "");
|
item.ShortId = GetQueryDecoded(query, "mtu");
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
282
v2rayN/ServiceLib/Manager/ActionPrecheckManager.cs
Normal file
282
v2rayN/ServiceLib/Manager/ActionPrecheckManager.cs
Normal file
|
@ -0,0 +1,282 @@
|
||||||
|
namespace ServiceLib.Manager;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Centralized pre-checks before sensitive actions (set active profile, generate config, etc.).
|
||||||
|
/// </summary>
|
||||||
|
public class ActionPrecheckManager(Config config)
|
||||||
|
{
|
||||||
|
private static readonly Lazy<ActionPrecheckManager> _instance = new(() => new ActionPrecheckManager(AppManager.Instance.Config));
|
||||||
|
public static ActionPrecheckManager Instance => _instance.Value;
|
||||||
|
|
||||||
|
private readonly Config _config = config;
|
||||||
|
|
||||||
|
public async Task<List<string>> Check(string? indexId)
|
||||||
|
{
|
||||||
|
if (indexId.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
return [ResUI.PleaseSelectServer];
|
||||||
|
}
|
||||||
|
|
||||||
|
var item = await AppManager.Instance.GetProfileItem(indexId);
|
||||||
|
if (item is null)
|
||||||
|
{
|
||||||
|
return [ResUI.PleaseSelectServer];
|
||||||
|
}
|
||||||
|
|
||||||
|
return await Check(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<string>> Check(ProfileItem? item)
|
||||||
|
{
|
||||||
|
if (item is null)
|
||||||
|
{
|
||||||
|
return [ResUI.PleaseSelectServer];
|
||||||
|
}
|
||||||
|
|
||||||
|
var errors = new List<string>();
|
||||||
|
|
||||||
|
errors.AddRange(await ValidateCurrentNodeAndCoreSupport(item));
|
||||||
|
errors.AddRange(await ValidateRelatedNodesExistAndValid(item));
|
||||||
|
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<List<string>> ValidateCurrentNodeAndCoreSupport(ProfileItem item)
|
||||||
|
{
|
||||||
|
if (item.ConfigType == EConfigType.Custom)
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
var coreType = AppManager.Instance.GetCoreType(item, item.ConfigType);
|
||||||
|
return await ValidateNodeAndCoreSupport(item, coreType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<List<string>> ValidateNodeAndCoreSupport(ProfileItem item, ECoreType? coreType = null)
|
||||||
|
{
|
||||||
|
var errors = new List<string>();
|
||||||
|
|
||||||
|
coreType ??= AppManager.Instance.GetCoreType(item, item.ConfigType);
|
||||||
|
|
||||||
|
if (item.ConfigType is EConfigType.Custom)
|
||||||
|
{
|
||||||
|
errors.Add(string.Format(ResUI.NotSupportProtocol, item.ConfigType.ToString()));
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!item.IsComplex())
|
||||||
|
{
|
||||||
|
if (item.Address.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
errors.Add(string.Format(ResUI.InvalidProperty, "Address"));
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.Port is <= 0 or >= 65536)
|
||||||
|
{
|
||||||
|
errors.Add(string.Format(ResUI.InvalidProperty, "Port"));
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (item.ConfigType)
|
||||||
|
{
|
||||||
|
case EConfigType.VMess:
|
||||||
|
if (item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id))
|
||||||
|
errors.Add(string.Format(ResUI.InvalidProperty, "Id"));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EConfigType.VLESS:
|
||||||
|
if (item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id) && item.Id.Length > 30)
|
||||||
|
errors.Add(string.Format(ResUI.InvalidProperty, "Id"));
|
||||||
|
if (!Global.Flows.Contains(item.Flow))
|
||||||
|
errors.Add(string.Format(ResUI.InvalidProperty, "Flow"));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EConfigType.Shadowsocks:
|
||||||
|
if (item.Id.IsNullOrEmpty())
|
||||||
|
errors.Add(string.Format(ResUI.InvalidProperty, "Id"));
|
||||||
|
if (string.IsNullOrEmpty(item.Security) || !Global.SsSecuritiesInSingbox.Contains(item.Security))
|
||||||
|
errors.Add(string.Format(ResUI.InvalidProperty, "Security"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.ConfigType is EConfigType.VLESS or EConfigType.Trojan
|
||||||
|
&& item.StreamSecurity == Global.StreamSecurityReality
|
||||||
|
&& item.PublicKey.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
errors.Add(string.Format(ResUI.InvalidProperty, "PublicKey"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errors.Count > 0)
|
||||||
|
{
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.ConfigType.IsGroupType())
|
||||||
|
{
|
||||||
|
ProfileGroupItemManager.Instance.TryGet(item.IndexId, out var group);
|
||||||
|
if (group is null || group.ChildItems.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
errors.Add(string.Format(ResUI.GroupEmpty, item.Remarks));
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
var hasCycle = ProfileGroupItemManager.HasCycle(item.IndexId);
|
||||||
|
if (hasCycle)
|
||||||
|
{
|
||||||
|
errors.Add(string.Format(ResUI.GroupSelfReference, item.Remarks));
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var child in Utils.String2List(group.ChildItems))
|
||||||
|
{
|
||||||
|
var childErrors = new List<string>();
|
||||||
|
if (child.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var childItem = await AppManager.Instance.GetProfileItem(child);
|
||||||
|
if (childItem is null)
|
||||||
|
{
|
||||||
|
childErrors.Add(string.Format(ResUI.NodeTagNotExist, child));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (childItem.ConfigType is EConfigType.Custom or EConfigType.ProxyChain)
|
||||||
|
{
|
||||||
|
childErrors.Add(string.Format(ResUI.InvalidProperty, childItem.Remarks));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
childErrors.AddRange(await ValidateNodeAndCoreSupport(childItem, coreType));
|
||||||
|
errors.AddRange(childErrors);
|
||||||
|
}
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
var net = item.GetNetwork() ?? item.Network;
|
||||||
|
|
||||||
|
if (coreType == ECoreType.sing_box)
|
||||||
|
{
|
||||||
|
// sing-box does not support xhttp / kcp
|
||||||
|
// sing-box does not support transports like ws/http/httpupgrade/etc. when the node is not vmess/trojan/vless
|
||||||
|
if (net is nameof(ETransport.kcp) or nameof(ETransport.xhttp))
|
||||||
|
{
|
||||||
|
errors.Add(string.Format(ResUI.CoreNotSupportNetwork, nameof(ECoreType.sing_box), net));
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.ConfigType is not (EConfigType.VMess or EConfigType.VLESS or EConfigType.Trojan))
|
||||||
|
{
|
||||||
|
if (net is nameof(ETransport.ws) or nameof(ETransport.http) or nameof(ETransport.h2) or nameof(ETransport.quic) or nameof(ETransport.httpupgrade))
|
||||||
|
{
|
||||||
|
errors.Add(string.Format(ResUI.CoreNotSupportProtocolTransport, nameof(ECoreType.sing_box), item.ConfigType.ToString(), net));
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (coreType is ECoreType.Xray)
|
||||||
|
{
|
||||||
|
// Xray core does not support these protocols
|
||||||
|
if (!Global.XraySupportConfigType.Contains(item.ConfigType)
|
||||||
|
&& !item.IsComplex())
|
||||||
|
{
|
||||||
|
errors.Add(string.Format(ResUI.CoreNotSupportProtocol, nameof(ECoreType.Xray), item.ConfigType.ToString()));
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<List<string>> ValidateRelatedNodesExistAndValid(ProfileItem? item)
|
||||||
|
{
|
||||||
|
var errors = new List<string>();
|
||||||
|
errors.AddRange(await ValidateProxyChainedNodeExistAndValid(item));
|
||||||
|
errors.AddRange(await ValidateRoutingNodeExistAndValid(item));
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<List<string>> ValidateProxyChainedNodeExistAndValid(ProfileItem? item)
|
||||||
|
{
|
||||||
|
var errors = new List<string>();
|
||||||
|
if (item is null)
|
||||||
|
{
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
// prev node and next node
|
||||||
|
var subItem = await AppManager.Instance.GetSubItem(item.Subid);
|
||||||
|
if (subItem is null)
|
||||||
|
{
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
var prevNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
||||||
|
var nextNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.NextProfile);
|
||||||
|
var coreType = AppManager.Instance.GetCoreType(item, item.ConfigType);
|
||||||
|
|
||||||
|
await CollectProxyChainedNodeValidation(prevNode, subItem.PrevProfile, coreType, errors);
|
||||||
|
await CollectProxyChainedNodeValidation(nextNode, subItem.NextProfile, coreType, errors);
|
||||||
|
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CollectProxyChainedNodeValidation(ProfileItem? node, string tag, ECoreType coreType, List<string> errors)
|
||||||
|
{
|
||||||
|
if (node is not null)
|
||||||
|
{
|
||||||
|
var nodeErrors = await ValidateNodeAndCoreSupport(node, coreType);
|
||||||
|
errors.AddRange(nodeErrors.Select(s => ResUI.ProxyChainedPrefix + s));
|
||||||
|
}
|
||||||
|
else if (tag.IsNotEmpty())
|
||||||
|
{
|
||||||
|
errors.Add(ResUI.ProxyChainedPrefix + string.Format(ResUI.NodeTagNotExist, tag));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<List<string>> ValidateRoutingNodeExistAndValid(ProfileItem? item)
|
||||||
|
{
|
||||||
|
var errors = new List<string>();
|
||||||
|
|
||||||
|
if (item is null)
|
||||||
|
{
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
var coreType = AppManager.Instance.GetCoreType(item, item.ConfigType);
|
||||||
|
var routing = await ConfigHandler.GetDefaultRouting(_config);
|
||||||
|
if (routing == null)
|
||||||
|
{
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet);
|
||||||
|
foreach (var ruleItem in rules ?? [])
|
||||||
|
{
|
||||||
|
if (!ruleItem.Enabled)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var outboundTag = ruleItem.OutboundTag;
|
||||||
|
if (outboundTag.IsNullOrEmpty() || Global.OutboundTags.Contains(outboundTag))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tagItem = await AppManager.Instance.GetProfileItemViaRemarks(outboundTag);
|
||||||
|
if (tagItem is null)
|
||||||
|
{
|
||||||
|
errors.Add(ResUI.RoutingRuleOutboundPrefix + string.Format(ResUI.NodeTagNotExist, outboundTag));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tagErrors = await ValidateNodeAndCoreSupport(tagItem, coreType);
|
||||||
|
errors.AddRange(tagErrors.Select(s => ResUI.RoutingRuleOutboundPrefix + s));
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,7 +8,6 @@ public sealed class AppManager
|
||||||
private Config _config;
|
private Config _config;
|
||||||
private int? _statePort;
|
private int? _statePort;
|
||||||
private int? _statePort2;
|
private int? _statePort2;
|
||||||
private Job? _processJob;
|
|
||||||
public static AppManager Instance => _instance.Value;
|
public static AppManager Instance => _instance.Value;
|
||||||
public Config Config => _config;
|
public Config Config => _config;
|
||||||
|
|
||||||
|
@ -65,6 +64,7 @@ public sealed class AppManager
|
||||||
SQLiteHelper.Instance.CreateTable<ProfileExItem>();
|
SQLiteHelper.Instance.CreateTable<ProfileExItem>();
|
||||||
SQLiteHelper.Instance.CreateTable<DNSItem>();
|
SQLiteHelper.Instance.CreateTable<DNSItem>();
|
||||||
SQLiteHelper.Instance.CreateTable<FullConfigTemplateItem>();
|
SQLiteHelper.Instance.CreateTable<FullConfigTemplateItem>();
|
||||||
|
SQLiteHelper.Instance.CreateTable<ProfileGroupItem>();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,21 +136,6 @@ public sealed class AppManager
|
||||||
return localPort + (int)protocol;
|
return localPort + (int)protocol;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddProcess(nint processHandle)
|
|
||||||
{
|
|
||||||
if (Utils.IsWindows())
|
|
||||||
{
|
|
||||||
_processJob ??= new();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_processJob?.AddProcess(processHandle);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Config
|
#endregion Config
|
||||||
|
|
||||||
#region SqliteHelper
|
#region SqliteHelper
|
||||||
|
@ -223,6 +208,15 @@ public sealed class AppManager
|
||||||
return await SQLiteHelper.Instance.TableAsync<ProfileItem>().FirstOrDefaultAsync(it => it.Remarks == remarks);
|
return await SQLiteHelper.Instance.TableAsync<ProfileItem>().FirstOrDefaultAsync(it => it.Remarks == remarks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<ProfileGroupItem?> GetProfileGroupItem(string indexId)
|
||||||
|
{
|
||||||
|
if (indexId.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return await SQLiteHelper.Instance.TableAsync<ProfileGroupItem>().FirstOrDefaultAsync(it => it.IndexId == indexId);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<List<RoutingItem>?> RoutingItems()
|
public async Task<List<RoutingItem>?> RoutingItems()
|
||||||
{
|
{
|
||||||
return await SQLiteHelper.Instance.TableAsync<RoutingItem>().OrderBy(t => t.Sort).ToListAsync();
|
return await SQLiteHelper.Instance.TableAsync<RoutingItem>().OrderBy(t => t.Sort).ToListAsync();
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using CliWrap;
|
using CliWrap;
|
||||||
using CliWrap.Buffered;
|
using CliWrap.Buffered;
|
||||||
|
@ -31,7 +30,7 @@ public class CoreAdminManager
|
||||||
await _updateFunc?.Invoke(notify, msg);
|
await _updateFunc?.Invoke(notify, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Process?> RunProcessAsLinuxSudo(string fileName, CoreInfo coreInfo, string configPath)
|
public async Task<ProcessService?> RunProcessAsLinuxSudo(string fileName, CoreInfo coreInfo, string configPath)
|
||||||
{
|
{
|
||||||
StringBuilder sb = new();
|
StringBuilder sb = new();
|
||||||
sb.AppendLine("#!/bin/bash");
|
sb.AppendLine("#!/bin/bash");
|
||||||
|
@ -39,50 +38,25 @@ public class CoreAdminManager
|
||||||
sb.AppendLine($"sudo -S {cmdLine}");
|
sb.AppendLine($"sudo -S {cmdLine}");
|
||||||
var shFilePath = await FileManager.CreateLinuxShellFile("run_as_sudo.sh", sb.ToString(), true);
|
var shFilePath = await FileManager.CreateLinuxShellFile("run_as_sudo.sh", sb.ToString(), true);
|
||||||
|
|
||||||
Process proc = new()
|
var procService = new ProcessService(
|
||||||
{
|
fileName: shFilePath,
|
||||||
StartInfo = new()
|
arguments: "",
|
||||||
{
|
workingDirectory: Utils.GetBinConfigPath(),
|
||||||
FileName = shFilePath,
|
displayLog: true,
|
||||||
Arguments = "",
|
redirectInput: true,
|
||||||
WorkingDirectory = Utils.GetBinConfigPath(),
|
environmentVars: null,
|
||||||
UseShellExecute = false,
|
updateFunc: _updateFunc
|
||||||
RedirectStandardInput = true,
|
);
|
||||||
RedirectStandardOutput = true,
|
|
||||||
RedirectStandardError = true,
|
|
||||||
CreateNoWindow = true,
|
|
||||||
StandardOutputEncoding = Encoding.UTF8,
|
|
||||||
StandardErrorEncoding = Encoding.UTF8,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void dataHandler(object sender, DataReceivedEventArgs e)
|
await procService.StartAsync(AppManager.Instance.LinuxSudoPwd);
|
||||||
{
|
|
||||||
if (e.Data.IsNotEmpty())
|
|
||||||
{
|
|
||||||
_ = UpdateFunc(false, e.Data + Environment.NewLine);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
proc.OutputDataReceived += dataHandler;
|
if (procService is null or { HasExited: true })
|
||||||
proc.ErrorDataReceived += dataHandler;
|
|
||||||
|
|
||||||
proc.Start();
|
|
||||||
proc.BeginOutputReadLine();
|
|
||||||
proc.BeginErrorReadLine();
|
|
||||||
|
|
||||||
await Task.Delay(10);
|
|
||||||
await proc.StandardInput.WriteLineAsync(AppManager.Instance.LinuxSudoPwd);
|
|
||||||
|
|
||||||
await Task.Delay(100);
|
|
||||||
if (proc is null or { HasExited: true })
|
|
||||||
{
|
{
|
||||||
throw new Exception(ResUI.FailedToRunCore);
|
throw new Exception(ResUI.FailedToRunCore);
|
||||||
}
|
}
|
||||||
|
_linuxSudoPid = procService.Id;
|
||||||
|
|
||||||
_linuxSudoPid = proc.Id;
|
return procService;
|
||||||
|
|
||||||
return proc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task KillProcessAsLinuxSudo()
|
public async Task KillProcessAsLinuxSudo()
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace ServiceLib.Manager;
|
namespace ServiceLib.Manager;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -11,8 +8,9 @@ public class CoreManager
|
||||||
private static readonly Lazy<CoreManager> _instance = new(() => new());
|
private static readonly Lazy<CoreManager> _instance = new(() => new());
|
||||||
public static CoreManager Instance => _instance.Value;
|
public static CoreManager Instance => _instance.Value;
|
||||||
private Config _config;
|
private Config _config;
|
||||||
private Process? _process;
|
private WindowsJob? _processJob;
|
||||||
private Process? _processPre;
|
private ProcessService? _processService;
|
||||||
|
private ProcessService? _processPreService;
|
||||||
private bool _linuxSudo = false;
|
private bool _linuxSudo = false;
|
||||||
private Func<bool, string, Task>? _updateFunc;
|
private Func<bool, string, Task>? _updateFunc;
|
||||||
private const string _tag = "CoreHandler";
|
private const string _tag = "CoreHandler";
|
||||||
|
@ -89,43 +87,37 @@ public class CoreManager
|
||||||
|
|
||||||
await CoreStart(node);
|
await CoreStart(node);
|
||||||
await CoreStartPreService(node);
|
await CoreStartPreService(node);
|
||||||
if (_process != null)
|
if (_processService != null)
|
||||||
{
|
{
|
||||||
await UpdateFunc(true, $"{node.GetSummary()}");
|
await UpdateFunc(true, $"{node.GetSummary()}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<int> LoadCoreConfigSpeedtest(List<ServerTestItem> selecteds)
|
public async Task<ProcessService?> LoadCoreConfigSpeedtest(List<ServerTestItem> selecteds)
|
||||||
{
|
{
|
||||||
var coreType = selecteds.Exists(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls) ? ECoreType.sing_box : ECoreType.Xray;
|
var coreType = selecteds.Any(t => Global.SingboxOnlyConfigType.Contains(t.ConfigType)) ? ECoreType.sing_box : ECoreType.Xray;
|
||||||
var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false));
|
var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false));
|
||||||
var configPath = Utils.GetBinConfigPath(fileName);
|
var configPath = Utils.GetBinConfigPath(fileName);
|
||||||
var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, configPath, selecteds, coreType);
|
var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, configPath, selecteds, coreType);
|
||||||
await UpdateFunc(false, result.Msg);
|
await UpdateFunc(false, result.Msg);
|
||||||
if (result.Success != true)
|
if (result.Success != true)
|
||||||
{
|
{
|
||||||
return -1;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
await UpdateFunc(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
|
await UpdateFunc(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
|
||||||
await UpdateFunc(false, configPath);
|
await UpdateFunc(false, configPath);
|
||||||
|
|
||||||
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(coreType);
|
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(coreType);
|
||||||
var proc = await RunProcess(coreInfo, fileName, true, false);
|
return await RunProcess(coreInfo, fileName, true, false);
|
||||||
if (proc is null)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return proc.Id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<int> LoadCoreConfigSpeedtest(ServerTestItem testItem)
|
public async Task<ProcessService?> LoadCoreConfigSpeedtest(ServerTestItem testItem)
|
||||||
{
|
{
|
||||||
var node = await AppManager.Instance.GetProfileItem(testItem.IndexId);
|
var node = await AppManager.Instance.GetProfileItem(testItem.IndexId);
|
||||||
if (node is null)
|
if (node is null)
|
||||||
{
|
{
|
||||||
return -1;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false));
|
var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false));
|
||||||
|
@ -133,18 +125,12 @@ public class CoreManager
|
||||||
var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, node, testItem, configPath);
|
var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, node, testItem, configPath);
|
||||||
if (result.Success != true)
|
if (result.Success != true)
|
||||||
{
|
{
|
||||||
return -1;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var coreType = AppManager.Instance.GetCoreType(node, node.ConfigType);
|
var coreType = AppManager.Instance.GetCoreType(node, node.ConfigType);
|
||||||
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(coreType);
|
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(coreType);
|
||||||
var proc = await RunProcess(coreInfo, fileName, true, false);
|
return await RunProcess(coreInfo, fileName, true, false);
|
||||||
if (proc is null)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return proc.Id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task CoreStop()
|
public async Task CoreStop()
|
||||||
|
@ -157,16 +143,18 @@ public class CoreManager
|
||||||
_linuxSudo = false;
|
_linuxSudo = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_process != null)
|
if (_processService != null)
|
||||||
{
|
{
|
||||||
await ProcUtils.ProcessKill(_process, Utils.IsWindows());
|
await _processService.StopAsync();
|
||||||
_process = null;
|
_processService.Dispose();
|
||||||
|
_processService = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_processPre != null)
|
if (_processPreService != null)
|
||||||
{
|
{
|
||||||
await ProcUtils.ProcessKill(_processPre, Utils.IsWindows());
|
await _processPreService.StopAsync();
|
||||||
_processPre = null;
|
_processPreService.Dispose();
|
||||||
|
_processPreService = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
@ -188,12 +176,12 @@ public class CoreManager
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_process = proc;
|
_processService = proc;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task CoreStartPreService(ProfileItem node)
|
private async Task CoreStartPreService(ProfileItem node)
|
||||||
{
|
{
|
||||||
if (_process != null && !_process.HasExited)
|
if (_processService != null && !_processService.HasExited)
|
||||||
{
|
{
|
||||||
var coreType = AppManager.Instance.GetCoreType(node, node.ConfigType);
|
var coreType = AppManager.Instance.GetCoreType(node, node.ConfigType);
|
||||||
var itemSocks = await ConfigHandler.GetPreSocksItem(_config, node, coreType);
|
var itemSocks = await ConfigHandler.GetPreSocksItem(_config, node, coreType);
|
||||||
|
@ -210,7 +198,7 @@ public class CoreManager
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_processPre = proc;
|
_processPreService = proc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -225,7 +213,7 @@ public class CoreManager
|
||||||
|
|
||||||
#region Process
|
#region Process
|
||||||
|
|
||||||
private async Task<Process?> RunProcess(CoreInfo? coreInfo, string configPath, bool displayLog, bool mayNeedSudo)
|
private async Task<ProcessService?> RunProcess(CoreInfo? coreInfo, string configPath, bool displayLog, bool mayNeedSudo)
|
||||||
{
|
{
|
||||||
var fileName = CoreInfoManager.Instance.GetCoreExecFile(coreInfo, out var msg);
|
var fileName = CoreInfoManager.Instance.GetCoreExecFile(coreInfo, out var msg);
|
||||||
if (fileName.IsNullOrEmpty())
|
if (fileName.IsNullOrEmpty())
|
||||||
|
@ -256,55 +244,48 @@ public class CoreManager
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<Process?> RunProcessNormal(string fileName, CoreInfo? coreInfo, string configPath, bool displayLog)
|
private async Task<ProcessService?> RunProcessNormal(string fileName, CoreInfo? coreInfo, string configPath, bool displayLog)
|
||||||
{
|
{
|
||||||
Process proc = new()
|
var environmentVars = new Dictionary<string, string>();
|
||||||
{
|
|
||||||
StartInfo = new()
|
|
||||||
{
|
|
||||||
FileName = fileName,
|
|
||||||
Arguments = string.Format(coreInfo.Arguments, coreInfo.AbsolutePath ? Utils.GetBinConfigPath(configPath).AppendQuotes() : configPath),
|
|
||||||
WorkingDirectory = Utils.GetBinConfigPath(),
|
|
||||||
UseShellExecute = false,
|
|
||||||
RedirectStandardOutput = displayLog,
|
|
||||||
RedirectStandardError = displayLog,
|
|
||||||
CreateNoWindow = true,
|
|
||||||
StandardOutputEncoding = displayLog ? Encoding.UTF8 : null,
|
|
||||||
StandardErrorEncoding = displayLog ? Encoding.UTF8 : null,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
foreach (var kv in coreInfo.Environment)
|
foreach (var kv in coreInfo.Environment)
|
||||||
{
|
{
|
||||||
proc.StartInfo.Environment[kv.Key] = string.Format(kv.Value, coreInfo.AbsolutePath ? Utils.GetBinConfigPath(configPath).AppendQuotes() : configPath);
|
environmentVars[kv.Key] = string.Format(kv.Value, coreInfo.AbsolutePath ? Utils.GetBinConfigPath(configPath).AppendQuotes() : configPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (displayLog)
|
var procService = new ProcessService(
|
||||||
{
|
fileName: fileName,
|
||||||
void dataHandler(object sender, DataReceivedEventArgs e)
|
arguments: string.Format(coreInfo.Arguments, coreInfo.AbsolutePath ? Utils.GetBinConfigPath(configPath).AppendQuotes() : configPath),
|
||||||
{
|
workingDirectory: Utils.GetBinConfigPath(),
|
||||||
if (e.Data.IsNotEmpty())
|
displayLog: displayLog,
|
||||||
{
|
redirectInput: false,
|
||||||
_ = UpdateFunc(false, e.Data + Environment.NewLine);
|
environmentVars: environmentVars,
|
||||||
}
|
updateFunc: _updateFunc
|
||||||
}
|
);
|
||||||
proc.OutputDataReceived += dataHandler;
|
|
||||||
proc.ErrorDataReceived += dataHandler;
|
|
||||||
}
|
|
||||||
proc.Start();
|
|
||||||
|
|
||||||
if (displayLog)
|
await procService.StartAsync();
|
||||||
{
|
|
||||||
proc.BeginOutputReadLine();
|
|
||||||
proc.BeginErrorReadLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
await Task.Delay(100);
|
await Task.Delay(100);
|
||||||
AppManager.Instance.AddProcess(proc.Handle);
|
|
||||||
if (proc is null or { HasExited: true })
|
if (procService is null or { HasExited: true })
|
||||||
{
|
{
|
||||||
throw new Exception(ResUI.FailedToRunCore);
|
throw new Exception(ResUI.FailedToRunCore);
|
||||||
}
|
}
|
||||||
return proc;
|
AddProcessJob(procService.Handle);
|
||||||
|
|
||||||
|
return procService;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddProcessJob(nint processHandle)
|
||||||
|
{
|
||||||
|
if (Utils.IsWindows())
|
||||||
|
{
|
||||||
|
_processJob ??= new();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_processJob?.AddProcess(processHandle);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Process
|
#endregion Process
|
||||||
|
|
286
v2rayN/ServiceLib/Manager/ProfileGroupItemManager.cs
Normal file
286
v2rayN/ServiceLib/Manager/ProfileGroupItemManager.cs
Normal file
|
@ -0,0 +1,286 @@
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
|
namespace ServiceLib.Manager;
|
||||||
|
|
||||||
|
public class ProfileGroupItemManager
|
||||||
|
{
|
||||||
|
private static readonly Lazy<ProfileGroupItemManager> _instance = new(() => new());
|
||||||
|
private ConcurrentDictionary<string, ProfileGroupItem> _items = new();
|
||||||
|
|
||||||
|
public static ProfileGroupItemManager Instance => _instance.Value;
|
||||||
|
private static readonly string _tag = "ProfileGroupItemManager";
|
||||||
|
|
||||||
|
private ProfileGroupItemManager()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Init()
|
||||||
|
{
|
||||||
|
await InitData();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read-only getters: do not create or mark dirty
|
||||||
|
public bool TryGet(string indexId, out ProfileGroupItem? item)
|
||||||
|
{
|
||||||
|
item = null;
|
||||||
|
if (string.IsNullOrWhiteSpace(indexId))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _items.TryGetValue(indexId, out item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProfileGroupItem? GetOrDefault(string indexId)
|
||||||
|
{
|
||||||
|
return string.IsNullOrWhiteSpace(indexId) ? null : (_items.TryGetValue(indexId, out var v) ? v : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task InitData()
|
||||||
|
{
|
||||||
|
await SQLiteHelper.Instance.ExecuteAsync($"delete from ProfileGroupItem where IndexId not in ( select indexId from ProfileItem )");
|
||||||
|
|
||||||
|
var list = await SQLiteHelper.Instance.TableAsync<ProfileGroupItem>().ToListAsync();
|
||||||
|
_items = new ConcurrentDictionary<string, ProfileGroupItem>(list.Where(t => !string.IsNullOrEmpty(t.IndexId)).ToDictionary(t => t.IndexId!));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ProfileGroupItem AddProfileGroupItem(string indexId)
|
||||||
|
{
|
||||||
|
var profileGroupItem = new ProfileGroupItem()
|
||||||
|
{
|
||||||
|
IndexId = indexId,
|
||||||
|
ChildItems = string.Empty,
|
||||||
|
MultipleLoad = EMultipleLoad.LeastPing
|
||||||
|
};
|
||||||
|
|
||||||
|
_items[indexId] = profileGroupItem;
|
||||||
|
return profileGroupItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ProfileGroupItem GetProfileGroupItem(string indexId)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(indexId))
|
||||||
|
{
|
||||||
|
indexId = Utils.GetGuid(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _items.GetOrAdd(indexId, AddProfileGroupItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ClearAll()
|
||||||
|
{
|
||||||
|
await SQLiteHelper.Instance.ExecuteAsync($"delete from ProfileGroupItem ");
|
||||||
|
_items.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SaveTo()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var lstExists = await SQLiteHelper.Instance.TableAsync<ProfileGroupItem>().ToListAsync();
|
||||||
|
var existsMap = lstExists.Where(t => !string.IsNullOrEmpty(t.IndexId)).ToDictionary(t => t.IndexId!);
|
||||||
|
|
||||||
|
var lstInserts = new List<ProfileGroupItem>();
|
||||||
|
var lstUpdates = new List<ProfileGroupItem>();
|
||||||
|
|
||||||
|
foreach (var item in _items.Values)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(item.IndexId))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existsMap.ContainsKey(item.IndexId))
|
||||||
|
{
|
||||||
|
lstUpdates.Add(item);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lstInserts.Add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (lstInserts.Count > 0)
|
||||||
|
{
|
||||||
|
await SQLiteHelper.Instance.InsertAllAsync(lstInserts);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lstUpdates.Count > 0)
|
||||||
|
{
|
||||||
|
await SQLiteHelper.Instance.UpdateAllAsync(lstUpdates);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logging.SaveLog(_tag, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logging.SaveLog(_tag, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProfileGroupItem GetOrCreateAndMarkDirty(string indexId)
|
||||||
|
{
|
||||||
|
return GetProfileGroupItem(indexId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask DisposeAsync()
|
||||||
|
{
|
||||||
|
await SaveTo();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SaveItemAsync(ProfileGroupItem item)
|
||||||
|
{
|
||||||
|
if (item is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(item.IndexId))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("IndexId required", nameof(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
_items[item.IndexId] = item;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var lst = await SQLiteHelper.Instance.TableAsync<ProfileGroupItem>().Where(t => t.IndexId == item.IndexId).ToListAsync();
|
||||||
|
if (lst != null && lst.Count > 0)
|
||||||
|
{
|
||||||
|
await SQLiteHelper.Instance.UpdateAllAsync(new List<ProfileGroupItem> { item });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await SQLiteHelper.Instance.InsertAllAsync(new List<ProfileGroupItem> { item });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logging.SaveLog(_tag, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Helper
|
||||||
|
|
||||||
|
public static bool HasCycle(string? indexId)
|
||||||
|
{
|
||||||
|
return HasCycle(indexId, new HashSet<string>(), new HashSet<string>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool HasCycle(string? indexId, HashSet<string> visited, HashSet<string> stack)
|
||||||
|
{
|
||||||
|
if (indexId.IsNullOrEmpty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (stack.Contains(indexId))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (visited.Contains(indexId))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
visited.Add(indexId);
|
||||||
|
stack.Add(indexId);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Instance.TryGet(indexId, out var groupItem);
|
||||||
|
|
||||||
|
if (groupItem == null || groupItem.ChildItems.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var childIds = Utils.String2List(groupItem.ChildItems)
|
||||||
|
.Where(p => !string.IsNullOrEmpty(p))
|
||||||
|
.ToList();
|
||||||
|
if (childIds == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var child in childIds)
|
||||||
|
{
|
||||||
|
if (HasCycle(child, visited, stack))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
stack.Remove(indexId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<(List<ProfileItem> Items, ProfileGroupItem? Group)> GetChildProfileItems(string? indexId)
|
||||||
|
{
|
||||||
|
Instance.TryGet(indexId, out var profileGroupItem);
|
||||||
|
if (profileGroupItem == null || profileGroupItem.ChildItems.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
return (new List<ProfileItem>(), profileGroupItem);
|
||||||
|
}
|
||||||
|
var items = await GetChildProfileItems(profileGroupItem);
|
||||||
|
return (items, profileGroupItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<List<ProfileItem>> GetChildProfileItems(ProfileGroupItem? group)
|
||||||
|
{
|
||||||
|
if (group == null || group.ChildItems.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
return new();
|
||||||
|
}
|
||||||
|
var childProfiles = (await Task.WhenAll(
|
||||||
|
Utils.String2List(group.ChildItems)
|
||||||
|
.Where(p => !p.IsNullOrEmpty())
|
||||||
|
.Select(AppManager.Instance.GetProfileItem)
|
||||||
|
))
|
||||||
|
.Where(p =>
|
||||||
|
p != null &&
|
||||||
|
p.IsValid() &&
|
||||||
|
p.ConfigType != EConfigType.Custom
|
||||||
|
)
|
||||||
|
.ToList();
|
||||||
|
return childProfiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<HashSet<string>> GetAllChildDomainAddresses(string indexId)
|
||||||
|
{
|
||||||
|
// include grand children
|
||||||
|
var childAddresses = new HashSet<string>();
|
||||||
|
if (!Instance.TryGet(indexId, out var groupItem) || groupItem.ChildItems.IsNullOrEmpty())
|
||||||
|
return childAddresses;
|
||||||
|
|
||||||
|
var childIds = Utils.String2List(groupItem.ChildItems);
|
||||||
|
|
||||||
|
foreach (var childId in childIds)
|
||||||
|
{
|
||||||
|
var childNode = await AppManager.Instance.GetProfileItem(childId);
|
||||||
|
if (childNode == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!childNode.IsComplex())
|
||||||
|
{
|
||||||
|
childAddresses.Add(childNode.Address);
|
||||||
|
}
|
||||||
|
else if (childNode.ConfigType.IsGroupType())
|
||||||
|
{
|
||||||
|
var subAddresses = await GetAllChildDomainAddresses(childNode.IndexId);
|
||||||
|
foreach (var addr in subAddresses)
|
||||||
|
{
|
||||||
|
childAddresses.Add(addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return childAddresses;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Helper
|
||||||
|
}
|
|
@ -264,8 +264,6 @@ public class SimpleDNSItem
|
||||||
public bool? BlockBindingQuery { get; set; }
|
public bool? BlockBindingQuery { get; set; }
|
||||||
public string? DirectDNS { get; set; }
|
public string? DirectDNS { get; set; }
|
||||||
public string? RemoteDNS { get; set; }
|
public string? RemoteDNS { get; set; }
|
||||||
public string? SingboxOutboundsResolveDNS { get; set; }
|
|
||||||
public string? SingboxFinalResolveDNS { get; set; }
|
|
||||||
public string? RayStrategy4Freedom { get; set; }
|
public string? RayStrategy4Freedom { get; set; }
|
||||||
public string? SingboxStrategy4Direct { get; set; }
|
public string? SingboxStrategy4Direct { get; set; }
|
||||||
public string? SingboxStrategy4Proxy { get; set; }
|
public string? SingboxStrategy4Proxy { get; set; }
|
||||||
|
|
14
v2rayN/ServiceLib/Models/ProfileGroupItem.cs
Normal file
14
v2rayN/ServiceLib/Models/ProfileGroupItem.cs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
using SQLite;
|
||||||
|
|
||||||
|
namespace ServiceLib.Models;
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class ProfileGroupItem
|
||||||
|
{
|
||||||
|
[PrimaryKey]
|
||||||
|
public string IndexId { get; set; }
|
||||||
|
|
||||||
|
public string ChildItems { get; set; }
|
||||||
|
|
||||||
|
public EMultipleLoad MultipleLoad { get; set; } = EMultipleLoad.LeastPing;
|
||||||
|
}
|
|
@ -32,18 +32,21 @@ public class ProfileItem : ReactiveObject
|
||||||
public string GetSummary()
|
public string GetSummary()
|
||||||
{
|
{
|
||||||
var summary = $"[{(ConfigType).ToString()}] ";
|
var summary = $"[{(ConfigType).ToString()}] ";
|
||||||
var arrAddr = Address.Contains(':') ? Address.Split(':') : Address.Split('.');
|
if (IsComplex())
|
||||||
var addr = arrAddr.Length switch
|
|
||||||
{
|
{
|
||||||
> 2 => $"{arrAddr.First()}***{arrAddr.Last()}",
|
summary += $"[{CoreType.ToString()}]{Remarks}";
|
||||||
> 1 => $"***{arrAddr.Last()}",
|
}
|
||||||
_ => Address
|
else
|
||||||
};
|
|
||||||
summary += ConfigType switch
|
|
||||||
{
|
{
|
||||||
EConfigType.Custom => $"[{CoreType.ToString()}]{Remarks}",
|
var arrAddr = Address.Contains(':') ? Address.Split(':') : Address.Split('.');
|
||||||
_ => $"{Remarks}({addr}:{Port})"
|
var addr = arrAddr.Length switch
|
||||||
};
|
{
|
||||||
|
> 2 => $"{arrAddr.First()}***{arrAddr.Last()}",
|
||||||
|
> 1 => $"***{arrAddr.Last()}",
|
||||||
|
_ => Address
|
||||||
|
};
|
||||||
|
summary += $"{Remarks}({addr}:{Port})";
|
||||||
|
}
|
||||||
return summary;
|
return summary;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,6 +64,51 @@ public class ProfileItem : ReactiveObject
|
||||||
return Network.TrimEx();
|
return Network.TrimEx();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsComplex()
|
||||||
|
{
|
||||||
|
return ConfigType.IsComplexType();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsValid()
|
||||||
|
{
|
||||||
|
if (IsComplex())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (Address.IsNullOrEmpty() || Port is <= 0 or >= 65536)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (ConfigType)
|
||||||
|
{
|
||||||
|
case EConfigType.VMess:
|
||||||
|
if (Id.IsNullOrEmpty() || !Utils.IsGuidByParse(Id))
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EConfigType.VLESS:
|
||||||
|
if (Id.IsNullOrEmpty() || (!Utils.IsGuidByParse(Id) && Id.Length > 30))
|
||||||
|
return false;
|
||||||
|
if (!Global.Flows.Contains(Flow))
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EConfigType.Shadowsocks:
|
||||||
|
if (Id.IsNullOrEmpty())
|
||||||
|
return false;
|
||||||
|
if (string.IsNullOrEmpty(Security) || !Global.SsSecuritiesInSingbox.Contains(Security))
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ConfigType is EConfigType.VLESS or EConfigType.Trojan)
|
||||||
|
&& StreamSecurity == Global.StreamSecurityReality
|
||||||
|
&& PublicKey.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
#endregion function
|
#endregion function
|
||||||
|
|
||||||
[PrimaryKey]
|
[PrimaryKey]
|
||||||
|
|
|
@ -15,4 +15,5 @@ public class RulesItem
|
||||||
public List<string>? Process { get; set; }
|
public List<string>? Process { get; set; }
|
||||||
public bool Enabled { get; set; } = true;
|
public bool Enabled { get; set; } = true;
|
||||||
public string? Remarks { get; set; }
|
public string? Remarks { get; set; }
|
||||||
|
public ERuleType? RuleType { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,4 +7,5 @@ public class RulesItemModel : RulesItem
|
||||||
public string Ips { get; set; }
|
public string Ips { get; set; }
|
||||||
public string Domains { get; set; }
|
public string Domains { get; set; }
|
||||||
public string Protocols { get; set; }
|
public string Protocols { get; set; }
|
||||||
|
public string RuleTypeName { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -145,6 +145,7 @@ public class Outbound4Sbox : BaseServer4Sbox
|
||||||
public string? plugin_opts { get; set; }
|
public string? plugin_opts { get; set; }
|
||||||
public List<string>? outbounds { get; set; }
|
public List<string>? outbounds { get; set; }
|
||||||
public bool? interrupt_exist_connections { get; set; }
|
public bool? interrupt_exist_connections { get; set; }
|
||||||
|
public int? tolerance { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Endpoints4Sbox : BaseServer4Sbox
|
public class Endpoints4Sbox : BaseServer4Sbox
|
||||||
|
|
474
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
474
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
|
@ -114,6 +114,33 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Core '{0}' does not support network type '{1}'. 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string CoreNotSupportNetwork {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("CoreNotSupportNetwork", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Core '{0}' does not support protocol '{1}'. 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string CoreNotSupportProtocol {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("CoreNotSupportProtocol", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Core '{0}' does not support protocol '{1}' when using transport '{2}'. 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string CoreNotSupportProtocolTransport {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("CoreNotSupportProtocolTransport", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Note that custom configuration relies entirely on your own configuration and does not work with all settings. If you want to use the system proxy, please modify the listening port manually. 的本地化字符串。
|
/// 查找类似 Note that custom configuration relies entirely on your own configuration and does not work with all settings. If you want to use the system proxy, please modify the listening port manually. 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -267,6 +294,24 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Group '{0}' is empty. Please add at least one node. 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string GroupEmpty {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("GroupEmpty", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 {0} Group cannot reference itself or have a circular reference 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string GroupSelfReference {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("GroupSelfReference", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 This is not the correct configuration, please check 的本地化字符串。
|
/// 查找类似 This is not the correct configuration, please check 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -294,6 +339,15 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 The {0} property is invalid, please check. 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string InvalidProperty {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("InvalidProperty", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Invalid address (URL) 的本地化字符串。
|
/// 查找类似 Invalid address (URL) 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -672,6 +726,15 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Add Child Configuration 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string menuAddChildServer {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("menuAddChildServer", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Add a custom configuration Configuration 的本地化字符串。
|
/// 查找类似 Add a custom configuration Configuration 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -699,6 +762,24 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Add Policy Group Configuration 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string menuAddPolicyGroupServer {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("menuAddPolicyGroupServer", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Add Proxy Chain Configuration 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string menuAddProxyChainServer {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("menuAddProxyChainServer", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Import Share Links from clipboard (Ctrl+V) 的本地化字符串。
|
/// 查找类似 Import Share Links from clipboard (Ctrl+V) 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -951,6 +1032,78 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Generate Policy Group from Multiple Profiles 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string menuGenGroupMultipleServer {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("menuGenGroupMultipleServer", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Multi-Configuration Fallback by sing-box 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string menuGenGroupMultipleServerSingBoxFallback {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("menuGenGroupMultipleServerSingBoxFallback", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Multi-Configuration LeastPing by sing-box 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string menuGenGroupMultipleServerSingBoxLeastPing {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("menuGenGroupMultipleServerSingBoxLeastPing", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Multi-Configuration Fallback by Xray 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string menuGenGroupMultipleServerXrayFallback {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("menuGenGroupMultipleServerXrayFallback", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Multi-Configuration LeastLoad by Xray 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string menuGenGroupMultipleServerXrayLeastLoad {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("menuGenGroupMultipleServerXrayLeastLoad", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Multi-Configuration LeastPing by Xray 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string menuGenGroupMultipleServerXrayLeastPing {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("menuGenGroupMultipleServerXrayLeastPing", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Multi-Configuration Random by Xray 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string menuGenGroupMultipleServerXrayRandom {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("menuGenGroupMultipleServerXrayRandom", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Multi-Configuration RoundRobin by Xray 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string menuGenGroupMultipleServerXrayRoundRobin {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("menuGenGroupMultipleServerXrayRoundRobin", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Global Hotkey Setting 的本地化字符串。
|
/// 查找类似 Global Hotkey Setting 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1320,6 +1473,15 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Remove Child Configuration 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string menuRemoveChildServer {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("menuRemoveChildServer", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Remove duplicate Configurations 的本地化字符串。
|
/// 查找类似 Remove duplicate Configurations 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1473,6 +1635,15 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Server List 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string menuServerList {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("menuServerList", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Configurations 的本地化字符串。
|
/// 查找类似 Configurations 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1482,60 +1653,6 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 查找类似 Multi-Configuration to custom configuration 的本地化字符串。
|
|
||||||
/// </summary>
|
|
||||||
public static string menuSetDefaultMultipleServer {
|
|
||||||
get {
|
|
||||||
return ResourceManager.GetString("menuSetDefaultMultipleServer", resourceCulture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 查找类似 Multi-Configuration LeastPing by sing-box 的本地化字符串。
|
|
||||||
/// </summary>
|
|
||||||
public static string menuSetDefaultMultipleServerSingBoxLeastPing {
|
|
||||||
get {
|
|
||||||
return ResourceManager.GetString("menuSetDefaultMultipleServerSingBoxLeastPing", resourceCulture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 查找类似 Multi-Configuration LeastLoad by Xray 的本地化字符串。
|
|
||||||
/// </summary>
|
|
||||||
public static string menuSetDefaultMultipleServerXrayLeastLoad {
|
|
||||||
get {
|
|
||||||
return ResourceManager.GetString("menuSetDefaultMultipleServerXrayLeastLoad", resourceCulture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 查找类似 Multi-Configuration LeastPing by Xray 的本地化字符串。
|
|
||||||
/// </summary>
|
|
||||||
public static string menuSetDefaultMultipleServerXrayLeastPing {
|
|
||||||
get {
|
|
||||||
return ResourceManager.GetString("menuSetDefaultMultipleServerXrayLeastPing", resourceCulture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 查找类似 Multi-Configuration Random by Xray 的本地化字符串。
|
|
||||||
/// </summary>
|
|
||||||
public static string menuSetDefaultMultipleServerXrayRandom {
|
|
||||||
get {
|
|
||||||
return ResourceManager.GetString("menuSetDefaultMultipleServerXrayRandom", resourceCulture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 查找类似 Multi-Configuration RoundRobin by Xray 的本地化字符串。
|
|
||||||
/// </summary>
|
|
||||||
public static string menuSetDefaultMultipleServerXrayRoundRobin {
|
|
||||||
get {
|
|
||||||
return ResourceManager.GetString("menuSetDefaultMultipleServerXrayRoundRobin", resourceCulture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Set as active Configuration (Enter) 的本地化字符串。
|
/// 查找类似 Set as active Configuration (Enter) 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1941,6 +2058,15 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Node alias '{0}' does not exist. 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string NodeTagNotExist {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("NodeTagNotExist", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Non-VMess or SS protocol 的本地化字符串。
|
/// 查找类似 Non-VMess or SS protocol 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1968,6 +2094,15 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Not support protocol '{0}'. 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string NotSupportProtocol {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("NotSupportProtocol", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Scan completed, no valid QR code found 的本地化字符串。
|
/// 查找类似 Scan completed, no valid QR code found 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1995,6 +2130,15 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Please Add At Least One Configuration 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string PleaseAddAtLeastOneServer {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("PleaseAddAtLeastOneServer", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Please fill Remarks 的本地化字符串。
|
/// 查找类似 Please fill Remarks 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -2040,6 +2184,24 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Policy group: 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string PolicyGroupPrefix {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("PolicyGroupPrefix", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Proxy chained: 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string ProxyChainedPrefix {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("ProxyChainedPrefix", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Global hotkey {0} registration failed, reason: {1} 的本地化字符串。
|
/// 查找类似 Global hotkey {0} registration failed, reason: {1} 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -2103,6 +2265,15 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Routing rule outbound: 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string RoutingRuleOutboundPrefix {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("RoutingRuleOutboundPrefix", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Run as Admin 的本地化字符串。
|
/// 查找类似 Run as Admin 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -2338,7 +2509,7 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Prevent domain-based routing rules from failing 的本地化字符串。
|
/// 查找类似 Block ECH and HTTP/3 availability checks when enabled 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string TbBlockSVCBHTTPSQueriesTips {
|
public static string TbBlockSVCBHTTPSQueriesTips {
|
||||||
get {
|
get {
|
||||||
|
@ -2373,6 +2544,24 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Policy Group 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbConfigTypePolicyGroup {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbConfigTypePolicyGroup", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Proxy Chain 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbConfigTypeProxyChain {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbConfigTypeProxyChain", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Confirm 的本地化字符串。
|
/// 查找类似 Confirm 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -2418,6 +2607,24 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 V2ray Custom DNS 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbCustomDnsRay {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbCustomDnsRay", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 sing-box Custom DNS 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbCustomDnsSingbox {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbCustomDnsSingbox", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Display GUI 的本地化字符串。
|
/// 查找类似 Display GUI 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -2526,6 +2733,15 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Fallback 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbFallback {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbFallback", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Fingerprint 的本地化字符串。
|
/// 查找类似 Fingerprint 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -2643,6 +2859,24 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Most Stable 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbLeastLoad {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbLeastLoad", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Lowest Latency 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbLeastPing {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbLeastPing", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Address (IPv4, IPv6) 的本地化字符串。
|
/// 查找类似 Address (IPv4, IPv6) 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -2697,6 +2931,15 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Policy Group Type 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbPolicyGroupType {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbPolicyGroupType", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Port 的本地化字符串。
|
/// 查找类似 Port 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -2769,6 +3012,15 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Random 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbRandom {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbRandom", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 v2ray Full Config Template 的本地化字符串。
|
/// 查找类似 v2ray Full Config Template 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -2805,6 +3057,15 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Via proxy — please ensure remote availability 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbRemoteDNSTips {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbRemoteDNSTips", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Camouflage domain(host) 的本地化字符串。
|
/// 查找类似 Camouflage domain(host) 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -2832,6 +3093,15 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Round Robin 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbRoundRobin {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbRoundRobin", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 socks: local port, socks2: second local port, socks3: LAN port 的本地化字符串。
|
/// 查找类似 socks: local port, socks2: second local port, socks3: LAN port 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -2913,6 +3183,24 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Rule Type 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbRuleType {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbRuleType", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 You can set separate rules for Routing and DNS, or select "ALL" to apply to both 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbRuleTypeTips {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbRuleTypeTips", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 sing-box Direct Resolution Strategy 的本地化字符串。
|
/// 查找类似 sing-box Direct Resolution Strategy 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -2922,33 +3210,6 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <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>
|
/// <summary>
|
||||||
/// 查找类似 sing-box Full Config Template 的本地化字符串。
|
/// 查找类似 sing-box Full Config Template 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -2967,24 +3228,6 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <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>
|
/// <summary>
|
||||||
/// 查找类似 sing-box Remote Resolution Strategy 的本地化字符串。
|
/// 查找类似 sing-box Remote Resolution Strategy 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -3120,24 +3363,6 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 查找类似 V2ray DNS settings 的本地化字符串。
|
|
||||||
/// </summary>
|
|
||||||
public static string TbSettingsCoreDns {
|
|
||||||
get {
|
|
||||||
return ResourceManager.GetString("TbSettingsCoreDns", resourceCulture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 查找类似 sing-box DNS settings 的本地化字符串。
|
|
||||||
/// </summary>
|
|
||||||
public static string TbSettingsCoreDnsSingbox {
|
|
||||||
get {
|
|
||||||
return ResourceManager.GetString("TbSettingsCoreDnsSingbox", resourceCulture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Core: KCP settings 的本地化字符串。
|
/// 查找类似 Core: KCP settings 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -3417,6 +3642,15 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 If the system does not have a tray function, please do not enable it 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbSettingsHide2TrayWhenCloseTip {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbSettingsHide2TrayWhenCloseTip", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Hysteria Max bandwidth (Up/Down) 的本地化字符串。
|
/// 查找类似 Hysteria Max bandwidth (Up/Down) 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -4014,9 +4248,9 @@ namespace ServiceLib.Resx {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 xray Freedom Resolution Strategy 的本地化字符串。
|
/// 查找类似 xray Freedom Resolution Strategy 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string TbXrayFreedomResolveStrategy {
|
public static string TbXrayFreedomStrategy {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("TbXrayFreedomResolveStrategy", resourceCulture);
|
return ResourceManager.GetString("TbXrayFreedomStrategy", resourceCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -675,8 +675,8 @@
|
||||||
<data name="TbSettingsCore" xml:space="preserve">
|
<data name="TbSettingsCore" xml:space="preserve">
|
||||||
<value>هسته: تنظیمات اولیه</value>
|
<value>هسته: تنظیمات اولیه</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsCoreDns" xml:space="preserve">
|
<data name="TbCustomDnsRay" xml:space="preserve">
|
||||||
<value>تنظیمات V2ray DNS</value>
|
<value>V2ray Custom DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsCoreKcp" xml:space="preserve">
|
<data name="TbSettingsCoreKcp" xml:space="preserve">
|
||||||
<value>هسته: تنظیمات KCP</value>
|
<value>هسته: تنظیمات KCP</value>
|
||||||
|
@ -1011,8 +1011,8 @@
|
||||||
<data name="menuDNSSetting" xml:space="preserve">
|
<data name="menuDNSSetting" xml:space="preserve">
|
||||||
<value>تنظیمات DNS</value>
|
<value>تنظیمات DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsCoreDnsSingbox" xml:space="preserve">
|
<data name="TbCustomDnsSingbox" xml:space="preserve">
|
||||||
<value>تنظیمات DNS sing-box</value>
|
<value>sing-box Custom DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbDnsSingboxObjectDoc" xml:space="preserve">
|
<data name="TbDnsSingboxObjectDoc" xml:space="preserve">
|
||||||
<value>لطفا ساختار DNS را پر کنید، برای مشاهده سند کلیک کنید</value>
|
<value>لطفا ساختار DNS را پر کنید، برای مشاهده سند کلیک کنید</value>
|
||||||
|
@ -1377,22 +1377,22 @@
|
||||||
<data name="TbPorts7Tips" xml:space="preserve">
|
<data name="TbPorts7Tips" xml:space="preserve">
|
||||||
<value>مخفی و پورت می شود، با کاما (،) جدا می شود</value>
|
<value>مخفی و پورت می شود، با کاما (،) جدا می شود</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetDefaultMultipleServer" xml:space="preserve">
|
<data name="menuGenGroupMultipleServer" xml:space="preserve">
|
||||||
<value>چند سرور به پیکربندی سفارشی</value>
|
<value>Generate Policy Group from Multiple Profiles</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetDefaultMultipleServerXrayRandom" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerXrayRandom" xml:space="preserve">
|
||||||
<value>چند سرور تصادفی توسط Xray</value>
|
<value>چند سرور تصادفی توسط Xray</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetDefaultMultipleServerXrayRoundRobin" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerXrayRoundRobin" xml:space="preserve">
|
||||||
<value>چند سرور RoundRobin توسط Xray</value>
|
<value>چند سرور RoundRobin توسط Xray</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetDefaultMultipleServerXrayLeastPing" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerXrayLeastPing" xml:space="preserve">
|
||||||
<value>چند سرور LeastPing توسط Xray</value>
|
<value>چند سرور LeastPing توسط Xray</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetDefaultMultipleServerXrayLeastLoad" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerXrayLeastLoad" xml:space="preserve">
|
||||||
<value>چند سرور LeastLoad توسط Xray</value>
|
<value>چند سرور LeastLoad توسط Xray</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetDefaultMultipleServerSingBoxLeastPing" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerSingBoxLeastPing" xml:space="preserve">
|
||||||
<value>LeastPing چند سرور توسط sing-box</value>
|
<value>LeastPing چند سرور توسط sing-box</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuExportConfig" xml:space="preserve">
|
<data name="menuExportConfig" xml:space="preserve">
|
||||||
|
@ -1419,19 +1419,10 @@
|
||||||
<data name="TbDomesticDNS" xml:space="preserve">
|
<data name="TbDomesticDNS" xml:space="preserve">
|
||||||
<value>Domestic DNS</value>
|
<value>Domestic DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBOutboundsResolverDNS" xml:space="preserve">
|
<data name="TbRemoteDNSTips" xml:space="preserve">
|
||||||
<value>Outbound DNS Resolution (sing-box)</value>
|
<value>Via proxy — please ensure remote availability</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBOutboundDomainResolve" xml:space="preserve">
|
<data name="TbXrayFreedomStrategy" 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>
|
<value>xray Freedom Resolution Strategy</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
|
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
|
||||||
|
@ -1443,9 +1434,6 @@
|
||||||
<data name="TbAddCommonDNSHosts" xml:space="preserve">
|
<data name="TbAddCommonDNSHosts" xml:space="preserve">
|
||||||
<value>Add Common DNS Hosts</value>
|
<value>Add Common DNS Hosts</value>
|
||||||
</data>
|
</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">
|
<data name="TbFakeIP" xml:space="preserve">
|
||||||
<value>FakeIP</value>
|
<value>FakeIP</value>
|
||||||
</data>
|
</data>
|
||||||
|
@ -1474,7 +1462,7 @@
|
||||||
<value>Custom DNS Enabled, This Page's Settings Invalid</value>
|
<value>Custom DNS Enabled, This Page's Settings Invalid</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve">
|
<data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve">
|
||||||
<value>Prevent domain-based routing rules from failing</value>
|
<value>Block ECH and HTTP/3 availability checks when enabled</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FillCorrectConfigTemplateText" xml:space="preserve">
|
<data name="FillCorrectConfigTemplateText" xml:space="preserve">
|
||||||
<value>Please fill in the correct config template</value>
|
<value>Please fill in the correct config template</value>
|
||||||
|
@ -1515,4 +1503,94 @@
|
||||||
<data name="TbFakeIPTips" xml:space="preserve">
|
<data name="TbFakeIPTips" xml:space="preserve">
|
||||||
<value>Applies globally by default, with built-in FakeIP filtering (sing-box only).</value>
|
<value>Applies globally by default, with built-in FakeIP filtering (sing-box only).</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="PleaseAddAtLeastOneServer" xml:space="preserve">
|
||||||
|
<value>Please Add At Least One Configuration</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbConfigTypePolicyGroup" xml:space="preserve">
|
||||||
|
<value>Policy Group</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbConfigTypeProxyChain" xml:space="preserve">
|
||||||
|
<value>Proxy Chain</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbLeastPing" xml:space="preserve">
|
||||||
|
<value>Lowest Latency</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbRandom" xml:space="preserve">
|
||||||
|
<value>Random</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbRoundRobin" xml:space="preserve">
|
||||||
|
<value>Round Robin</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbLeastLoad" xml:space="preserve">
|
||||||
|
<value>Most Stable</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbPolicyGroupType" xml:space="preserve">
|
||||||
|
<value>Policy Group Type</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuAddPolicyGroupServer" xml:space="preserve">
|
||||||
|
<value>Add Policy Group Configuration</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuAddProxyChainServer" xml:space="preserve">
|
||||||
|
<value>Add Proxy Chain Configuration</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuAddChildServer" xml:space="preserve">
|
||||||
|
<value>Add Child Configuration</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuRemoveChildServer" xml:space="preserve">
|
||||||
|
<value>Remove Child Configuration</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuServerList" xml:space="preserve">
|
||||||
|
<value>Server List</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbFallback" xml:space="preserve">
|
||||||
|
<value>Fallback</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
|
||||||
|
<value>Multi-Configuration Fallback by sing-box</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
|
||||||
|
<value>Multi-Configuration Fallback by Xray</value>
|
||||||
|
</data>
|
||||||
|
<data name="CoreNotSupportNetwork" xml:space="preserve">
|
||||||
|
<value>Core '{0}' does not support network type '{1}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="CoreNotSupportProtocolTransport" xml:space="preserve">
|
||||||
|
<value>Core '{0}' does not support protocol '{1}' when using transport '{2}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="CoreNotSupportProtocol" xml:space="preserve">
|
||||||
|
<value>Core '{0}' does not support protocol '{1}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ProxyChainedPrefix" xml:space="preserve">
|
||||||
|
<value>Proxy chained: </value>
|
||||||
|
</data>
|
||||||
|
<data name="RoutingRuleOutboundPrefix" xml:space="preserve">
|
||||||
|
<value>Routing rule outbound: </value>
|
||||||
|
</data>
|
||||||
|
<data name="PolicyGroupPrefix" xml:space="preserve">
|
||||||
|
<value>Policy group: </value>
|
||||||
|
</data>
|
||||||
|
<data name="NodeTagNotExist" xml:space="preserve">
|
||||||
|
<value>Node alias '{0}' does not exist.</value>
|
||||||
|
</data>
|
||||||
|
<data name="GroupEmpty" xml:space="preserve">
|
||||||
|
<value>Group '{0}' is empty. Please add at least one node.</value>
|
||||||
|
</data>
|
||||||
|
<data name="InvalidProperty" xml:space="preserve">
|
||||||
|
<value>The {0} property is invalid, please check.</value>
|
||||||
|
</data>
|
||||||
|
<data name="GroupSelfReference" xml:space="preserve">
|
||||||
|
<value>{0} Group cannot reference itself or have a circular reference</value>
|
||||||
|
</data>
|
||||||
|
<data name="NotSupportProtocol" xml:space="preserve">
|
||||||
|
<value>Not support protocol '{0}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbSettingsHide2TrayWhenCloseTip" xml:space="preserve">
|
||||||
|
<value>If the system does not have a tray function, please do not enable it</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbRuleType" xml:space="preserve">
|
||||||
|
<value>Rule Type</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbRuleTypeTips" xml:space="preserve">
|
||||||
|
<value>You can set separate rules for Routing and DNS, or select "ALL" to apply to both</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
|
@ -675,8 +675,8 @@
|
||||||
<data name="TbSettingsCore" xml:space="preserve">
|
<data name="TbSettingsCore" xml:space="preserve">
|
||||||
<value>Core: alapbeállítások</value>
|
<value>Core: alapbeállítások</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsCoreDns" xml:space="preserve">
|
<data name="TbCustomDnsRay" xml:space="preserve">
|
||||||
<value>V2ray DNS beállítások</value>
|
<value>V2ray Custom DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsCoreKcp" xml:space="preserve">
|
<data name="TbSettingsCoreKcp" xml:space="preserve">
|
||||||
<value>Core: KCP beállítások</value>
|
<value>Core: KCP beállítások</value>
|
||||||
|
@ -1011,8 +1011,8 @@
|
||||||
<data name="menuDNSSetting" xml:space="preserve">
|
<data name="menuDNSSetting" xml:space="preserve">
|
||||||
<value>DNS beállítások</value>
|
<value>DNS beállítások</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsCoreDnsSingbox" xml:space="preserve">
|
<data name="TbCustomDnsSingbox" xml:space="preserve">
|
||||||
<value>sing-box DNS beállítások</value>
|
<value>sing-box Custom DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbDnsSingboxObjectDoc" xml:space="preserve">
|
<data name="TbDnsSingboxObjectDoc" xml:space="preserve">
|
||||||
<value>Kérjük, töltse ki a DNS struktúrát, kattintson a dokumentum megtekintéséhez</value>
|
<value>Kérjük, töltse ki a DNS struktúrát, kattintson a dokumentum megtekintéséhez</value>
|
||||||
|
@ -1377,22 +1377,22 @@
|
||||||
<data name="TbPorts7Tips" xml:space="preserve">
|
<data name="TbPorts7Tips" xml:space="preserve">
|
||||||
<value>A portot lefedi, vesszővel (,) elválasztva</value>
|
<value>A portot lefedi, vesszővel (,) elválasztva</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetDefaultMultipleServer" xml:space="preserve">
|
<data name="menuGenGroupMultipleServer" xml:space="preserve">
|
||||||
<value>Több konfiguráció egyéni konfigurációra</value>
|
<value>Generate Policy Group from Multiple Profiles</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetDefaultMultipleServerXrayRandom" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerXrayRandom" xml:space="preserve">
|
||||||
<value>Több konfiguráció véletlenszerűen Xray szerint</value>
|
<value>Több konfiguráció véletlenszerűen Xray szerint</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetDefaultMultipleServerXrayRoundRobin" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerXrayRoundRobin" xml:space="preserve">
|
||||||
<value>Több konfiguráció RoundRobin Xray szerint</value>
|
<value>Több konfiguráció RoundRobin Xray szerint</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetDefaultMultipleServerXrayLeastPing" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerXrayLeastPing" xml:space="preserve">
|
||||||
<value>Több konfiguráció legkisebb pinggel Xray szerint</value>
|
<value>Több konfiguráció legkisebb pinggel Xray szerint</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetDefaultMultipleServerXrayLeastLoad" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerXrayLeastLoad" xml:space="preserve">
|
||||||
<value>Több konfiguráció legkisebb terheléssel Xray szerint</value>
|
<value>Több konfiguráció legkisebb terheléssel Xray szerint</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetDefaultMultipleServerSingBoxLeastPing" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerSingBoxLeastPing" xml:space="preserve">
|
||||||
<value>Több konfiguráció legkisebb pinggel sing-box szerint</value>
|
<value>Több konfiguráció legkisebb pinggel sing-box szerint</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuExportConfig" xml:space="preserve">
|
<data name="menuExportConfig" xml:space="preserve">
|
||||||
|
@ -1419,19 +1419,10 @@
|
||||||
<data name="TbDomesticDNS" xml:space="preserve">
|
<data name="TbDomesticDNS" xml:space="preserve">
|
||||||
<value>Domestic DNS</value>
|
<value>Domestic DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBOutboundsResolverDNS" xml:space="preserve">
|
<data name="TbRemoteDNSTips" xml:space="preserve">
|
||||||
<value>Outbound DNS Resolution (sing-box)</value>
|
<value>Via proxy — please ensure remote availability</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBOutboundDomainResolve" xml:space="preserve">
|
<data name="TbXrayFreedomStrategy" 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>
|
<value>xray Freedom Resolution Strategy</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
|
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
|
||||||
|
@ -1443,9 +1434,6 @@
|
||||||
<data name="TbAddCommonDNSHosts" xml:space="preserve">
|
<data name="TbAddCommonDNSHosts" xml:space="preserve">
|
||||||
<value>Add Common DNS Hosts</value>
|
<value>Add Common DNS Hosts</value>
|
||||||
</data>
|
</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">
|
<data name="TbFakeIP" xml:space="preserve">
|
||||||
<value>FakeIP</value>
|
<value>FakeIP</value>
|
||||||
</data>
|
</data>
|
||||||
|
@ -1474,7 +1462,7 @@
|
||||||
<value>Custom DNS Enabled, This Page's Settings Invalid</value>
|
<value>Custom DNS Enabled, This Page's Settings Invalid</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve">
|
<data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve">
|
||||||
<value>Prevent domain-based routing rules from failing</value>
|
<value>Block ECH and HTTP/3 availability checks when enabled</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FillCorrectConfigTemplateText" xml:space="preserve">
|
<data name="FillCorrectConfigTemplateText" xml:space="preserve">
|
||||||
<value>Please fill in the correct config template</value>
|
<value>Please fill in the correct config template</value>
|
||||||
|
@ -1515,4 +1503,94 @@
|
||||||
<data name="TbFakeIPTips" xml:space="preserve">
|
<data name="TbFakeIPTips" xml:space="preserve">
|
||||||
<value>Applies globally by default, with built-in FakeIP filtering (sing-box only).</value>
|
<value>Applies globally by default, with built-in FakeIP filtering (sing-box only).</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="PleaseAddAtLeastOneServer" xml:space="preserve">
|
||||||
|
<value>Please Add At Least One Configuration</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbConfigTypePolicyGroup" xml:space="preserve">
|
||||||
|
<value>Policy Group</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbConfigTypeProxyChain" xml:space="preserve">
|
||||||
|
<value>Proxy Chain</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbLeastPing" xml:space="preserve">
|
||||||
|
<value>Lowest Latency</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbRandom" xml:space="preserve">
|
||||||
|
<value>Random</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbRoundRobin" xml:space="preserve">
|
||||||
|
<value>Round Robin</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbLeastLoad" xml:space="preserve">
|
||||||
|
<value>Most Stable</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbPolicyGroupType" xml:space="preserve">
|
||||||
|
<value>Policy Group Type</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuAddPolicyGroupServer" xml:space="preserve">
|
||||||
|
<value>Add Policy Group Configuration</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuAddProxyChainServer" xml:space="preserve">
|
||||||
|
<value>Add Proxy Chain Configuration</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuAddChildServer" xml:space="preserve">
|
||||||
|
<value>Add Child Configuration</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuRemoveChildServer" xml:space="preserve">
|
||||||
|
<value>Remove Child Configuration</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuServerList" xml:space="preserve">
|
||||||
|
<value>Server List</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbFallback" xml:space="preserve">
|
||||||
|
<value>Fallback</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
|
||||||
|
<value>Multi-Configuration Fallback by sing-box</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
|
||||||
|
<value>Multi-Configuration Fallback by Xray</value>
|
||||||
|
</data>
|
||||||
|
<data name="CoreNotSupportNetwork" xml:space="preserve">
|
||||||
|
<value>Core '{0}' does not support network type '{1}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="CoreNotSupportProtocolTransport" xml:space="preserve">
|
||||||
|
<value>Core '{0}' does not support protocol '{1}' when using transport '{2}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="CoreNotSupportProtocol" xml:space="preserve">
|
||||||
|
<value>Core '{0}' does not support protocol '{1}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ProxyChainedPrefix" xml:space="preserve">
|
||||||
|
<value>Proxy chained: </value>
|
||||||
|
</data>
|
||||||
|
<data name="RoutingRuleOutboundPrefix" xml:space="preserve">
|
||||||
|
<value>Routing rule outbound: </value>
|
||||||
|
</data>
|
||||||
|
<data name="PolicyGroupPrefix" xml:space="preserve">
|
||||||
|
<value>Policy group: </value>
|
||||||
|
</data>
|
||||||
|
<data name="NodeTagNotExist" xml:space="preserve">
|
||||||
|
<value>Node alias '{0}' does not exist.</value>
|
||||||
|
</data>
|
||||||
|
<data name="GroupEmpty" xml:space="preserve">
|
||||||
|
<value>Group '{0}' is empty. Please add at least one node.</value>
|
||||||
|
</data>
|
||||||
|
<data name="InvalidProperty" xml:space="preserve">
|
||||||
|
<value>The {0} property is invalid, please check.</value>
|
||||||
|
</data>
|
||||||
|
<data name="GroupSelfReference" xml:space="preserve">
|
||||||
|
<value>{0} Group cannot reference itself or have a circular reference</value>
|
||||||
|
</data>
|
||||||
|
<data name="NotSupportProtocol" xml:space="preserve">
|
||||||
|
<value>Not support protocol '{0}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbSettingsHide2TrayWhenCloseTip" xml:space="preserve">
|
||||||
|
<value>If the system does not have a tray function, please do not enable it</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbRuleTypeTips" xml:space="preserve">
|
||||||
|
<value>You can set separate rules for Routing and DNS, or select "ALL" to apply to both</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbRuleType" xml:space="preserve">
|
||||||
|
<value>Rule Type</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
|
@ -675,8 +675,8 @@
|
||||||
<data name="TbSettingsCore" xml:space="preserve">
|
<data name="TbSettingsCore" xml:space="preserve">
|
||||||
<value>Core: basic settings</value>
|
<value>Core: basic settings</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsCoreDns" xml:space="preserve">
|
<data name="TbCustomDnsRay" xml:space="preserve">
|
||||||
<value>V2ray DNS settings</value>
|
<value>V2ray Custom DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsCoreKcp" xml:space="preserve">
|
<data name="TbSettingsCoreKcp" xml:space="preserve">
|
||||||
<value>Core: KCP settings</value>
|
<value>Core: KCP settings</value>
|
||||||
|
@ -1011,8 +1011,8 @@
|
||||||
<data name="menuDNSSetting" xml:space="preserve">
|
<data name="menuDNSSetting" xml:space="preserve">
|
||||||
<value>DNS Settings</value>
|
<value>DNS Settings</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsCoreDnsSingbox" xml:space="preserve">
|
<data name="TbCustomDnsSingbox" xml:space="preserve">
|
||||||
<value>sing-box DNS settings</value>
|
<value>sing-box Custom DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbDnsSingboxObjectDoc" xml:space="preserve">
|
<data name="TbDnsSingboxObjectDoc" xml:space="preserve">
|
||||||
<value>Please fill in DNS Structure, Click to view the document</value>
|
<value>Please fill in DNS Structure, Click to view the document</value>
|
||||||
|
@ -1377,22 +1377,22 @@
|
||||||
<data name="TbPorts7Tips" xml:space="preserve">
|
<data name="TbPorts7Tips" xml:space="preserve">
|
||||||
<value>Will cover the port, separate with commas (,)</value>
|
<value>Will cover the port, separate with commas (,)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetDefaultMultipleServer" xml:space="preserve">
|
<data name="menuGenGroupMultipleServer" xml:space="preserve">
|
||||||
<value>Multi-Configuration to custom configuration</value>
|
<value>Generate Policy Group from Multiple Profiles</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetDefaultMultipleServerXrayRandom" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerXrayRandom" xml:space="preserve">
|
||||||
<value>Multi-Configuration Random by Xray</value>
|
<value>Multi-Configuration Random by Xray</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetDefaultMultipleServerXrayRoundRobin" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerXrayRoundRobin" xml:space="preserve">
|
||||||
<value>Multi-Configuration RoundRobin by Xray</value>
|
<value>Multi-Configuration RoundRobin by Xray</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetDefaultMultipleServerXrayLeastPing" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerXrayLeastPing" xml:space="preserve">
|
||||||
<value>Multi-Configuration LeastPing by Xray</value>
|
<value>Multi-Configuration LeastPing by Xray</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetDefaultMultipleServerXrayLeastLoad" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerXrayLeastLoad" xml:space="preserve">
|
||||||
<value>Multi-Configuration LeastLoad by Xray</value>
|
<value>Multi-Configuration LeastLoad by Xray</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetDefaultMultipleServerSingBoxLeastPing" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerSingBoxLeastPing" xml:space="preserve">
|
||||||
<value>Multi-Configuration LeastPing by sing-box</value>
|
<value>Multi-Configuration LeastPing by sing-box</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuExportConfig" xml:space="preserve">
|
<data name="menuExportConfig" xml:space="preserve">
|
||||||
|
@ -1419,19 +1419,10 @@
|
||||||
<data name="TbDomesticDNS" xml:space="preserve">
|
<data name="TbDomesticDNS" xml:space="preserve">
|
||||||
<value>Domestic DNS</value>
|
<value>Domestic DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBOutboundsResolverDNS" xml:space="preserve">
|
<data name="TbRemoteDNSTips" xml:space="preserve">
|
||||||
<value>Outbound DNS Resolution (sing-box)</value>
|
<value>Via proxy — please ensure remote availability</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBOutboundDomainResolve" xml:space="preserve">
|
<data name="TbXrayFreedomStrategy" 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>
|
<value>xray Freedom Resolution Strategy</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
|
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
|
||||||
|
@ -1443,9 +1434,6 @@
|
||||||
<data name="TbAddCommonDNSHosts" xml:space="preserve">
|
<data name="TbAddCommonDNSHosts" xml:space="preserve">
|
||||||
<value>Add Common DNS Hosts</value>
|
<value>Add Common DNS Hosts</value>
|
||||||
</data>
|
</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">
|
<data name="TbFakeIP" xml:space="preserve">
|
||||||
<value>FakeIP</value>
|
<value>FakeIP</value>
|
||||||
</data>
|
</data>
|
||||||
|
@ -1474,7 +1462,7 @@
|
||||||
<value>Custom DNS Enabled, This Page's Settings Invalid</value>
|
<value>Custom DNS Enabled, This Page's Settings Invalid</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve">
|
<data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve">
|
||||||
<value>Prevent domain-based routing rules from failing</value>
|
<value>Block ECH and HTTP/3 availability checks when enabled</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FillCorrectConfigTemplateText" xml:space="preserve">
|
<data name="FillCorrectConfigTemplateText" xml:space="preserve">
|
||||||
<value>Please fill in the correct config template</value>
|
<value>Please fill in the correct config template</value>
|
||||||
|
@ -1515,4 +1503,94 @@
|
||||||
<data name="TbFakeIPTips" xml:space="preserve">
|
<data name="TbFakeIPTips" xml:space="preserve">
|
||||||
<value>Applies globally by default, with built-in FakeIP filtering (sing-box only).</value>
|
<value>Applies globally by default, with built-in FakeIP filtering (sing-box only).</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="PleaseAddAtLeastOneServer" xml:space="preserve">
|
||||||
|
<value>Please Add At Least One Configuration</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbConfigTypePolicyGroup" xml:space="preserve">
|
||||||
|
<value>Policy Group</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbConfigTypeProxyChain" xml:space="preserve">
|
||||||
|
<value>Proxy Chain</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbLeastPing" xml:space="preserve">
|
||||||
|
<value>Lowest Latency</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbRandom" xml:space="preserve">
|
||||||
|
<value>Random</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbRoundRobin" xml:space="preserve">
|
||||||
|
<value>Round Robin</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbLeastLoad" xml:space="preserve">
|
||||||
|
<value>Most Stable</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbPolicyGroupType" xml:space="preserve">
|
||||||
|
<value>Policy Group Type</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuAddPolicyGroupServer" xml:space="preserve">
|
||||||
|
<value>Add Policy Group Configuration</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuAddProxyChainServer" xml:space="preserve">
|
||||||
|
<value>Add Proxy Chain Configuration</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuAddChildServer" xml:space="preserve">
|
||||||
|
<value>Add Child Configuration</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuRemoveChildServer" xml:space="preserve">
|
||||||
|
<value>Remove Child Configuration</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuServerList" xml:space="preserve">
|
||||||
|
<value>Server List</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbFallback" xml:space="preserve">
|
||||||
|
<value>Fallback</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
|
||||||
|
<value>Multi-Configuration Fallback by sing-box</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
|
||||||
|
<value>Multi-Configuration Fallback by Xray</value>
|
||||||
|
</data>
|
||||||
|
<data name="CoreNotSupportNetwork" xml:space="preserve">
|
||||||
|
<value>Core '{0}' does not support network type '{1}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="CoreNotSupportProtocolTransport" xml:space="preserve">
|
||||||
|
<value>Core '{0}' does not support protocol '{1}' when using transport '{2}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="CoreNotSupportProtocol" xml:space="preserve">
|
||||||
|
<value>Core '{0}' does not support protocol '{1}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ProxyChainedPrefix" xml:space="preserve">
|
||||||
|
<value>Proxy chained: </value>
|
||||||
|
</data>
|
||||||
|
<data name="RoutingRuleOutboundPrefix" xml:space="preserve">
|
||||||
|
<value>Routing rule outbound: </value>
|
||||||
|
</data>
|
||||||
|
<data name="PolicyGroupPrefix" xml:space="preserve">
|
||||||
|
<value>Policy group: </value>
|
||||||
|
</data>
|
||||||
|
<data name="NodeTagNotExist" xml:space="preserve">
|
||||||
|
<value>Node alias '{0}' does not exist.</value>
|
||||||
|
</data>
|
||||||
|
<data name="GroupEmpty" xml:space="preserve">
|
||||||
|
<value>Group '{0}' is empty. Please add at least one node.</value>
|
||||||
|
</data>
|
||||||
|
<data name="InvalidProperty" xml:space="preserve">
|
||||||
|
<value>The {0} property is invalid, please check.</value>
|
||||||
|
</data>
|
||||||
|
<data name="GroupSelfReference" xml:space="preserve">
|
||||||
|
<value>{0} Group cannot reference itself or have a circular reference</value>
|
||||||
|
</data>
|
||||||
|
<data name="NotSupportProtocol" xml:space="preserve">
|
||||||
|
<value>Not support protocol '{0}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbSettingsHide2TrayWhenCloseTip" xml:space="preserve">
|
||||||
|
<value>If the system does not have a tray function, please do not enable it</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbRuleTypeTips" xml:space="preserve">
|
||||||
|
<value>You can set separate rules for Routing and DNS, or select "ALL" to apply to both</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbRuleType" xml:space="preserve">
|
||||||
|
<value>Rule Type</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
|
@ -675,8 +675,8 @@
|
||||||
<data name="TbSettingsCore" xml:space="preserve">
|
<data name="TbSettingsCore" xml:space="preserve">
|
||||||
<value>Ядро: базовые настройки</value>
|
<value>Ядро: базовые настройки</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsCoreDns" xml:space="preserve">
|
<data name="TbCustomDnsRay" xml:space="preserve">
|
||||||
<value>Настройки DNS V2ray</value>
|
<value>V2ray Custom DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsCoreKcp" xml:space="preserve">
|
<data name="TbSettingsCoreKcp" xml:space="preserve">
|
||||||
<value>Ядро: настройки KCP</value>
|
<value>Ядро: настройки KCP</value>
|
||||||
|
@ -1011,8 +1011,8 @@
|
||||||
<data name="menuDNSSetting" xml:space="preserve">
|
<data name="menuDNSSetting" xml:space="preserve">
|
||||||
<value>Настройки DNS</value>
|
<value>Настройки DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsCoreDnsSingbox" xml:space="preserve">
|
<data name="TbCustomDnsSingbox" xml:space="preserve">
|
||||||
<value>Настройки DNS sing-box</value>
|
<value>sing-box Custom DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbDnsSingboxObjectDoc" xml:space="preserve">
|
<data name="TbDnsSingboxObjectDoc" xml:space="preserve">
|
||||||
<value>Заполните структуру DNS, нажмите, чтобы открыть документ</value>
|
<value>Заполните структуру DNS, нажмите, чтобы открыть документ</value>
|
||||||
|
@ -1377,22 +1377,22 @@
|
||||||
<data name="TbPorts7Tips" xml:space="preserve">
|
<data name="TbPorts7Tips" xml:space="preserve">
|
||||||
<value>Заменит указанный порт, перечисляйте через запятую (,)</value>
|
<value>Заменит указанный порт, перечисляйте через запятую (,)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetDefaultMultipleServer" xml:space="preserve">
|
<data name="menuGenGroupMultipleServer" xml:space="preserve">
|
||||||
<value>От мультиконфигурации к пользовательской конфигурации</value>
|
<value>Generate Policy Group from Multiple Profiles</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetDefaultMultipleServerXrayRandom" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerXrayRandom" xml:space="preserve">
|
||||||
<value>Случайный (Xray)</value>
|
<value>Случайный (Xray)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetDefaultMultipleServerXrayRoundRobin" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerXrayRoundRobin" xml:space="preserve">
|
||||||
<value>Круговой (Xray)</value>
|
<value>Круговой (Xray)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetDefaultMultipleServerXrayLeastPing" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerXrayLeastPing" xml:space="preserve">
|
||||||
<value>Минимальное RTT (минимальное время туда-обратно) (Xray)</value>
|
<value>Минимальное RTT (минимальное время туда-обратно) (Xray)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetDefaultMultipleServerXrayLeastLoad" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerXrayLeastLoad" xml:space="preserve">
|
||||||
<value>Минимальная нагрузка (Xray)</value>
|
<value>Минимальная нагрузка (Xray)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetDefaultMultipleServerSingBoxLeastPing" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerSingBoxLeastPing" xml:space="preserve">
|
||||||
<value>Минимальное RTT (минимальное время туда-обратно) (sing-box)</value>
|
<value>Минимальное RTT (минимальное время туда-обратно) (sing-box)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuExportConfig" xml:space="preserve">
|
<data name="menuExportConfig" xml:space="preserve">
|
||||||
|
@ -1419,19 +1419,10 @@
|
||||||
<data name="TbDomesticDNS" xml:space="preserve">
|
<data name="TbDomesticDNS" xml:space="preserve">
|
||||||
<value>Внутренний DNS</value>
|
<value>Внутренний DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBOutboundsResolverDNS" xml:space="preserve">
|
<data name="TbRemoteDNSTips" xml:space="preserve">
|
||||||
<value>Резолвер DNS для исходящих (sing-box)</value>
|
<value>Via proxy — please ensure remote availability</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBOutboundDomainResolve" xml:space="preserve">
|
<data name="TbXrayFreedomStrategy" xml:space="preserve">
|
||||||
<value>Разрешать домены для исходящих соединений</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSBDoHResolverServer" xml:space="preserve">
|
|
||||||
<value>Сервер DoH-резолвера (sing-box)</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbSBFallbackDNSResolve" xml:space="preserve">
|
|
||||||
<value>Резервное DNS-разрешение (рекомендуется указывать IP)</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbXrayFreedomResolveStrategy" xml:space="preserve">
|
|
||||||
<value>Стратегия резолвинга Freedom (Xray)</value>
|
<value>Стратегия резолвинга Freedom (Xray)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
|
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
|
||||||
|
@ -1443,9 +1434,6 @@
|
||||||
<data name="TbAddCommonDNSHosts" xml:space="preserve">
|
<data name="TbAddCommonDNSHosts" xml:space="preserve">
|
||||||
<value>Добавить стандартные записи hosts (DNS)</value>
|
<value>Добавить стандартные записи hosts (DNS)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBDoHOverride" xml:space="preserve">
|
|
||||||
<value>Сервер DoH-резолвера sing-box можно переопределить</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbFakeIP" xml:space="preserve">
|
<data name="TbFakeIP" xml:space="preserve">
|
||||||
<value>FakeIP</value>
|
<value>FakeIP</value>
|
||||||
</data>
|
</data>
|
||||||
|
@ -1474,7 +1462,7 @@
|
||||||
<value>Включён пользовательский DNS — настройки на этой странице не применяются</value>
|
<value>Включён пользовательский DNS — настройки на этой странице не применяются</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve">
|
<data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve">
|
||||||
<value>Предотвращает сбои доменных правил маршрутизации</value>
|
<value>Block ECH and HTTP/3 availability checks when enabled</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FillCorrectConfigTemplateText" xml:space="preserve">
|
<data name="FillCorrectConfigTemplateText" xml:space="preserve">
|
||||||
<value>Пожалуйста, заполните корректный шаблон конфигурации</value>
|
<value>Пожалуйста, заполните корректный шаблон конфигурации</value>
|
||||||
|
@ -1515,4 +1503,94 @@
|
||||||
<data name="TbFakeIPTips" xml:space="preserve">
|
<data name="TbFakeIPTips" xml:space="preserve">
|
||||||
<value>Applies globally by default, with built-in FakeIP filtering (sing-box only).</value>
|
<value>Applies globally by default, with built-in FakeIP filtering (sing-box only).</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="PleaseAddAtLeastOneServer" xml:space="preserve">
|
||||||
|
<value>Please Add At Least One Configuration</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbConfigTypePolicyGroup" xml:space="preserve">
|
||||||
|
<value>Policy Group</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbConfigTypeProxyChain" xml:space="preserve">
|
||||||
|
<value>Proxy Chain</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbLeastPing" xml:space="preserve">
|
||||||
|
<value>Lowest Latency</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbRandom" xml:space="preserve">
|
||||||
|
<value>Random</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbRoundRobin" xml:space="preserve">
|
||||||
|
<value>Round Robin</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbLeastLoad" xml:space="preserve">
|
||||||
|
<value>Most Stable</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbPolicyGroupType" xml:space="preserve">
|
||||||
|
<value>Policy Group Type</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuAddPolicyGroupServer" xml:space="preserve">
|
||||||
|
<value>Add Policy Group Configuration</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuAddProxyChainServer" xml:space="preserve">
|
||||||
|
<value>Add Proxy Chain Configuration</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuAddChildServer" xml:space="preserve">
|
||||||
|
<value>Add Child Configuration</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuRemoveChildServer" xml:space="preserve">
|
||||||
|
<value>Remove Child Configuration</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuServerList" xml:space="preserve">
|
||||||
|
<value>Server List</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbFallback" xml:space="preserve">
|
||||||
|
<value>Fallback</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
|
||||||
|
<value>Multi-Configuration Fallback by sing-box</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
|
||||||
|
<value>Multi-Configuration Fallback by Xray</value>
|
||||||
|
</data>
|
||||||
|
<data name="CoreNotSupportNetwork" xml:space="preserve">
|
||||||
|
<value>Core '{0}' does not support network type '{1}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="CoreNotSupportProtocolTransport" xml:space="preserve">
|
||||||
|
<value>Core '{0}' does not support protocol '{1}' when using transport '{2}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="CoreNotSupportProtocol" xml:space="preserve">
|
||||||
|
<value>Core '{0}' does not support protocol '{1}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ProxyChainedPrefix" xml:space="preserve">
|
||||||
|
<value>Proxy chained: </value>
|
||||||
|
</data>
|
||||||
|
<data name="RoutingRuleOutboundPrefix" xml:space="preserve">
|
||||||
|
<value>Routing rule outbound: </value>
|
||||||
|
</data>
|
||||||
|
<data name="PolicyGroupPrefix" xml:space="preserve">
|
||||||
|
<value>Policy group: </value>
|
||||||
|
</data>
|
||||||
|
<data name="NodeTagNotExist" xml:space="preserve">
|
||||||
|
<value>Node alias '{0}' does not exist.</value>
|
||||||
|
</data>
|
||||||
|
<data name="GroupEmpty" xml:space="preserve">
|
||||||
|
<value>Group '{0}' is empty. Please add at least one node.</value>
|
||||||
|
</data>
|
||||||
|
<data name="InvalidProperty" xml:space="preserve">
|
||||||
|
<value>The {0} property is invalid, please check.</value>
|
||||||
|
</data>
|
||||||
|
<data name="GroupSelfReference" xml:space="preserve">
|
||||||
|
<value>{0} Group cannot reference itself or have a circular reference</value>
|
||||||
|
</data>
|
||||||
|
<data name="NotSupportProtocol" xml:space="preserve">
|
||||||
|
<value>Not support protocol '{0}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbSettingsHide2TrayWhenCloseTip" xml:space="preserve">
|
||||||
|
<value>If the system does not have a tray function, please do not enable it</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbRuleTypeTips" xml:space="preserve">
|
||||||
|
<value>You can set separate rules for Routing and DNS, or select "ALL" to apply to both</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbRuleType" xml:space="preserve">
|
||||||
|
<value>Rule Type</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
|
@ -675,8 +675,8 @@
|
||||||
<data name="TbSettingsCore" xml:space="preserve">
|
<data name="TbSettingsCore" xml:space="preserve">
|
||||||
<value>Core: 基础设置</value>
|
<value>Core: 基础设置</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsCoreDns" xml:space="preserve">
|
<data name="TbCustomDnsRay" xml:space="preserve">
|
||||||
<value>v2ray DNS 设置</value>
|
<value>v2ray 自定义 DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsCoreKcp" xml:space="preserve">
|
<data name="TbSettingsCoreKcp" xml:space="preserve">
|
||||||
<value>Core: KCP 设置</value>
|
<value>Core: KCP 设置</value>
|
||||||
|
@ -1008,8 +1008,8 @@
|
||||||
<data name="menuDNSSetting" xml:space="preserve">
|
<data name="menuDNSSetting" xml:space="preserve">
|
||||||
<value>DNS 设置</value>
|
<value>DNS 设置</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsCoreDnsSingbox" xml:space="preserve">
|
<data name="TbCustomDnsSingbox" xml:space="preserve">
|
||||||
<value>sing-box DNS 设置</value>
|
<value>sing-box 自定义 DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbDnsSingboxObjectDoc" xml:space="preserve">
|
<data name="TbDnsSingboxObjectDoc" xml:space="preserve">
|
||||||
<value>请填写 DNS JSON 结构,点击查看文档</value>
|
<value>请填写 DNS JSON 结构,点击查看文档</value>
|
||||||
|
@ -1374,22 +1374,22 @@
|
||||||
<data name="TbPorts7Tips" xml:space="preserve">
|
<data name="TbPorts7Tips" xml:space="preserve">
|
||||||
<value>会覆盖端口,多组时用逗号 (,) 隔开</value>
|
<value>会覆盖端口,多组时用逗号 (,) 隔开</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetDefaultMultipleServer" xml:space="preserve">
|
<data name="menuGenGroupMultipleServer" xml:space="preserve">
|
||||||
<value>多配置文件产生自定义配置 (多选)</value>
|
<value>多配置文件生成策略组</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetDefaultMultipleServerXrayRandom" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerXrayRandom" xml:space="preserve">
|
||||||
<value>多配置文件随机 Xray</value>
|
<value>多配置文件随机 Xray</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetDefaultMultipleServerXrayRoundRobin" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerXrayRoundRobin" xml:space="preserve">
|
||||||
<value>多配置文件负载均衡 Xray</value>
|
<value>多配置文件负载均衡 Xray</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetDefaultMultipleServerXrayLeastPing" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerXrayLeastPing" xml:space="preserve">
|
||||||
<value>多配置文件最低延迟 Xray</value>
|
<value>多配置文件最低延迟 Xray</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetDefaultMultipleServerXrayLeastLoad" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerXrayLeastLoad" xml:space="preserve">
|
||||||
<value>多配置文件最稳定 Xray</value>
|
<value>多配置文件最稳定 Xray</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetDefaultMultipleServerSingBoxLeastPing" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerSingBoxLeastPing" xml:space="preserve">
|
||||||
<value>多配置文件最低延迟 sing-box</value>
|
<value>多配置文件最低延迟 sing-box</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuExportConfig" xml:space="preserve">
|
<data name="menuExportConfig" xml:space="preserve">
|
||||||
|
@ -1416,19 +1416,10 @@
|
||||||
<data name="TbDomesticDNS" xml:space="preserve">
|
<data name="TbDomesticDNS" xml:space="preserve">
|
||||||
<value>直连 DNS</value>
|
<value>直连 DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBOutboundsResolverDNS" xml:space="preserve">
|
<data name="TbRemoteDNSTips" xml:space="preserve">
|
||||||
<value>出站 DNS 解析(sing-box)</value>
|
<value>通过代理,请确保远程可用</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBOutboundDomainResolve" xml:space="preserve">
|
<data name="TbXrayFreedomStrategy" 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>
|
<value>xray freedom 解析策略</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
|
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
|
||||||
|
@ -1440,9 +1431,6 @@
|
||||||
<data name="TbAddCommonDNSHosts" xml:space="preserve">
|
<data name="TbAddCommonDNSHosts" xml:space="preserve">
|
||||||
<value>添加常用 DNS Hosts</value>
|
<value>添加常用 DNS Hosts</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBDoHOverride" xml:space="preserve">
|
|
||||||
<value>开启后可覆盖 sing-box DoH 解析服务器</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbFakeIP" xml:space="preserve">
|
<data name="TbFakeIP" xml:space="preserve">
|
||||||
<value>FakeIP</value>
|
<value>FakeIP</value>
|
||||||
</data>
|
</data>
|
||||||
|
@ -1471,7 +1459,7 @@
|
||||||
<value>自定义 DNS 已启用,此页面配置将无效</value>
|
<value>自定义 DNS 已启用,此页面配置将无效</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve">
|
<data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve">
|
||||||
<value>避免域名分流规则失效</value>
|
<value>开启后将阻止 ECH 和 HTTP/3 可用性查询</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FillCorrectConfigTemplateText" xml:space="preserve">
|
<data name="FillCorrectConfigTemplateText" xml:space="preserve">
|
||||||
<value>请填写正确的配置模板</value>
|
<value>请填写正确的配置模板</value>
|
||||||
|
@ -1512,4 +1500,94 @@
|
||||||
<data name="TbFakeIPTips" xml:space="preserve">
|
<data name="TbFakeIPTips" xml:space="preserve">
|
||||||
<value>默认全局生效,内置 FakeIP 过滤,仅在 sing-box 中生效</value>
|
<value>默认全局生效,内置 FakeIP 过滤,仅在 sing-box 中生效</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="PleaseAddAtLeastOneServer" xml:space="preserve">
|
||||||
|
<value>请至少添加一个配置文件</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbConfigTypePolicyGroup" xml:space="preserve">
|
||||||
|
<value>策略组</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbConfigTypeProxyChain" xml:space="preserve">
|
||||||
|
<value>链式代理</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbLeastPing" xml:space="preserve">
|
||||||
|
<value>最低延迟</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbRandom" xml:space="preserve">
|
||||||
|
<value>随机</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbRoundRobin" xml:space="preserve">
|
||||||
|
<value>负载均衡</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbLeastLoad" xml:space="preserve">
|
||||||
|
<value>最稳定</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbPolicyGroupType" xml:space="preserve">
|
||||||
|
<value>策略组类型</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuAddPolicyGroupServer" xml:space="preserve">
|
||||||
|
<value>添加策略组配置文件</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuAddProxyChainServer" xml:space="preserve">
|
||||||
|
<value>添加链式代理配置文件</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuAddChildServer" xml:space="preserve">
|
||||||
|
<value>添加子配置文件</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuRemoveChildServer" xml:space="preserve">
|
||||||
|
<value>删除子配置文件</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuServerList" xml:space="preserve">
|
||||||
|
<value>服务器列表</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbFallback" xml:space="preserve">
|
||||||
|
<value>故障转移</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
|
||||||
|
<value>多配置文件故障转移 sing-box</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
|
||||||
|
<value>多配置文件故障转移 Xray</value>
|
||||||
|
</data>
|
||||||
|
<data name="CoreNotSupportNetwork" xml:space="preserve">
|
||||||
|
<value>核心 '{0}' 不支持网络类型 '{1}'。</value>
|
||||||
|
</data>
|
||||||
|
<data name="CoreNotSupportProtocolTransport" xml:space="preserve">
|
||||||
|
<value>核心 '{0}' 在使用传输方式 '{2}' 时不支持协议 '{1}'。</value>
|
||||||
|
</data>
|
||||||
|
<data name="CoreNotSupportProtocol" xml:space="preserve">
|
||||||
|
<value>核心 '{0}' 不支持协议 '{1}'。</value>
|
||||||
|
</data>
|
||||||
|
<data name="ProxyChainedPrefix" xml:space="preserve">
|
||||||
|
<value>代理链: </value>
|
||||||
|
</data>
|
||||||
|
<data name="RoutingRuleOutboundPrefix" xml:space="preserve">
|
||||||
|
<value>路由规则出站: </value>
|
||||||
|
</data>
|
||||||
|
<data name="PolicyGroupPrefix" xml:space="preserve">
|
||||||
|
<value>策略组: </value>
|
||||||
|
</data>
|
||||||
|
<data name="NodeTagNotExist" xml:space="preserve">
|
||||||
|
<value>节点别名 '{0}' 不存在。</value>
|
||||||
|
</data>
|
||||||
|
<data name="GroupEmpty" xml:space="preserve">
|
||||||
|
<value>组“{0}”为空。请至少添加一个节点。</value>
|
||||||
|
</data>
|
||||||
|
<data name="InvalidProperty" xml:space="preserve">
|
||||||
|
<value>{0}属性无效,请检查</value>
|
||||||
|
</data>
|
||||||
|
<data name="GroupSelfReference" xml:space="preserve">
|
||||||
|
<value>{0} 分组不能引用自身或循环引用</value>
|
||||||
|
</data>
|
||||||
|
<data name="NotSupportProtocol" xml:space="preserve">
|
||||||
|
<value>不支持协议 '{0}'。</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbSettingsHide2TrayWhenCloseTip" xml:space="preserve">
|
||||||
|
<value>如果系统没有托盘功能,请不要开启</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbRuleType" xml:space="preserve">
|
||||||
|
<value>规则类型</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbRuleTypeTips" xml:space="preserve">
|
||||||
|
<value>可对 Routing 和 DNS 单独设定规则,ALL 则都生效</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
|
@ -675,8 +675,8 @@
|
||||||
<data name="TbSettingsCore" xml:space="preserve">
|
<data name="TbSettingsCore" xml:space="preserve">
|
||||||
<value>Core: 基礎設定</value>
|
<value>Core: 基礎設定</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsCoreDns" xml:space="preserve">
|
<data name="TbCustomDnsRay" xml:space="preserve">
|
||||||
<value>V2ray DNS 設定</value>
|
<value>V2ray Custom DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsCoreKcp" xml:space="preserve">
|
<data name="TbSettingsCoreKcp" xml:space="preserve">
|
||||||
<value>Core: KCP 設定</value>
|
<value>Core: KCP 設定</value>
|
||||||
|
@ -1008,8 +1008,8 @@
|
||||||
<data name="menuDNSSetting" xml:space="preserve">
|
<data name="menuDNSSetting" xml:space="preserve">
|
||||||
<value>DNS 設定</value>
|
<value>DNS 設定</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsCoreDnsSingbox" xml:space="preserve">
|
<data name="TbCustomDnsSingbox" xml:space="preserve">
|
||||||
<value>sing-box DNS 設定</value>
|
<value>sing-box Custom DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbDnsSingboxObjectDoc" xml:space="preserve">
|
<data name="TbDnsSingboxObjectDoc" xml:space="preserve">
|
||||||
<value>請填寫 DNS JSON 結構,點擊查看檔案</value>
|
<value>請填寫 DNS JSON 結構,點擊查看檔案</value>
|
||||||
|
@ -1374,22 +1374,22 @@
|
||||||
<data name="TbPorts7Tips" xml:space="preserve">
|
<data name="TbPorts7Tips" xml:space="preserve">
|
||||||
<value>會覆蓋埠,多組時用逗號 (,) 隔開</value>
|
<value>會覆蓋埠,多組時用逗號 (,) 隔開</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetDefaultMultipleServer" xml:space="preserve">
|
<data name="menuGenGroupMultipleServer" xml:space="preserve">
|
||||||
<value>多設定檔產生自訂配置 (多選)</value>
|
<value>Generate Policy Group from Multiple Profiles</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetDefaultMultipleServerXrayRandom" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerXrayRandom" xml:space="preserve">
|
||||||
<value>多設定檔隨機 Xray</value>
|
<value>多設定檔隨機 Xray</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetDefaultMultipleServerXrayRoundRobin" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerXrayRoundRobin" xml:space="preserve">
|
||||||
<value>多設定檔負載平衡 Xray</value>
|
<value>多設定檔負載平衡 Xray</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetDefaultMultipleServerXrayLeastPing" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerXrayLeastPing" xml:space="preserve">
|
||||||
<value>多設定檔最低延遲 Xray</value>
|
<value>多設定檔最低延遲 Xray</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetDefaultMultipleServerXrayLeastLoad" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerXrayLeastLoad" xml:space="preserve">
|
||||||
<value>多設定檔最穩定 Xray</value>
|
<value>多設定檔最穩定 Xray</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetDefaultMultipleServerSingBoxLeastPing" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerSingBoxLeastPing" xml:space="preserve">
|
||||||
<value>多設定檔最低延遲 sing-box</value>
|
<value>多設定檔最低延遲 sing-box</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuExportConfig" xml:space="preserve">
|
<data name="menuExportConfig" xml:space="preserve">
|
||||||
|
@ -1416,19 +1416,10 @@
|
||||||
<data name="TbDomesticDNS" xml:space="preserve">
|
<data name="TbDomesticDNS" xml:space="preserve">
|
||||||
<value>Domestic DNS</value>
|
<value>Domestic DNS</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBOutboundsResolverDNS" xml:space="preserve">
|
<data name="TbRemoteDNSTips" xml:space="preserve">
|
||||||
<value>Outbound DNS Resolution (sing-box)</value>
|
<value>Via proxy — please ensure remote availability</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBOutboundDomainResolve" xml:space="preserve">
|
<data name="TbXrayFreedomStrategy" 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>
|
<value>xray Freedom Resolution Strategy</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
|
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
|
||||||
|
@ -1440,9 +1431,6 @@
|
||||||
<data name="TbAddCommonDNSHosts" xml:space="preserve">
|
<data name="TbAddCommonDNSHosts" xml:space="preserve">
|
||||||
<value>Add Common DNS Hosts</value>
|
<value>Add Common DNS Hosts</value>
|
||||||
</data>
|
</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">
|
<data name="TbFakeIP" xml:space="preserve">
|
||||||
<value>FakeIP</value>
|
<value>FakeIP</value>
|
||||||
</data>
|
</data>
|
||||||
|
@ -1471,7 +1459,7 @@
|
||||||
<value>Custom DNS Enabled, This Page's Settings Invalid</value>
|
<value>Custom DNS Enabled, This Page's Settings Invalid</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve">
|
<data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve">
|
||||||
<value>Prevent domain-based routing rules from failing</value>
|
<value>Block ECH and HTTP/3 availability checks when enabled</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FillCorrectConfigTemplateText" xml:space="preserve">
|
<data name="FillCorrectConfigTemplateText" xml:space="preserve">
|
||||||
<value>Please fill in the correct config template</value>
|
<value>Please fill in the correct config template</value>
|
||||||
|
@ -1512,4 +1500,94 @@
|
||||||
<data name="TbFakeIPTips" xml:space="preserve">
|
<data name="TbFakeIPTips" xml:space="preserve">
|
||||||
<value>Applies globally by default, with built-in FakeIP filtering (sing-box only).</value>
|
<value>Applies globally by default, with built-in FakeIP filtering (sing-box only).</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="PleaseAddAtLeastOneServer" xml:space="preserve">
|
||||||
|
<value>Please Add At Least One Configuration</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbConfigTypePolicyGroup" xml:space="preserve">
|
||||||
|
<value>Policy Group</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbConfigTypeProxyChain" xml:space="preserve">
|
||||||
|
<value>Proxy Chain</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbLeastPing" xml:space="preserve">
|
||||||
|
<value>Lowest Latency</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbRandom" xml:space="preserve">
|
||||||
|
<value>Random</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbRoundRobin" xml:space="preserve">
|
||||||
|
<value>Round Robin</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbLeastLoad" xml:space="preserve">
|
||||||
|
<value>Most Stable</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbPolicyGroupType" xml:space="preserve">
|
||||||
|
<value>Policy Group Type</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuAddPolicyGroupServer" xml:space="preserve">
|
||||||
|
<value>Add Policy Group Configuration</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuAddProxyChainServer" xml:space="preserve">
|
||||||
|
<value>Add Proxy Chain Configuration</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuAddChildServer" xml:space="preserve">
|
||||||
|
<value>Add Child Configuration</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuRemoveChildServer" xml:space="preserve">
|
||||||
|
<value>Remove Child Configuration</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuServerList" xml:space="preserve">
|
||||||
|
<value>Server List</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbFallback" xml:space="preserve">
|
||||||
|
<value>Fallback</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
|
||||||
|
<value>Multi-Configuration Fallback by sing-box</value>
|
||||||
|
</data>
|
||||||
|
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
|
||||||
|
<value>Multi-Configuration Fallback by Xray</value>
|
||||||
|
</data>
|
||||||
|
<data name="CoreNotSupportNetwork" xml:space="preserve">
|
||||||
|
<value>Core '{0}' does not support network type '{1}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="CoreNotSupportProtocolTransport" xml:space="preserve">
|
||||||
|
<value>Core '{0}' does not support protocol '{1}' when using transport '{2}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="CoreNotSupportProtocol" xml:space="preserve">
|
||||||
|
<value>Core '{0}' does not support protocol '{1}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ProxyChainedPrefix" xml:space="preserve">
|
||||||
|
<value>Proxy chained: </value>
|
||||||
|
</data>
|
||||||
|
<data name="RoutingRuleOutboundPrefix" xml:space="preserve">
|
||||||
|
<value>Routing rule outbound: </value>
|
||||||
|
</data>
|
||||||
|
<data name="PolicyGroupPrefix" xml:space="preserve">
|
||||||
|
<value>Policy group: </value>
|
||||||
|
</data>
|
||||||
|
<data name="NodeTagNotExist" xml:space="preserve">
|
||||||
|
<value>Node alias '{0}' does not exist.</value>
|
||||||
|
</data>
|
||||||
|
<data name="GroupEmpty" xml:space="preserve">
|
||||||
|
<value>Group '{0}' is empty. Please add at least one node.</value>
|
||||||
|
</data>
|
||||||
|
<data name="InvalidProperty" xml:space="preserve">
|
||||||
|
<value>The {0} property is invalid, please check.</value>
|
||||||
|
</data>
|
||||||
|
<data name="GroupSelfReference" xml:space="preserve">
|
||||||
|
<value>{0} 分組不能引用自身或循環引用</value>
|
||||||
|
</data>
|
||||||
|
<data name="NotSupportProtocol" xml:space="preserve">
|
||||||
|
<value>Not support protocol '{0}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbSettingsHide2TrayWhenCloseTip" xml:space="preserve">
|
||||||
|
<value>如果系統沒有托盤功能,請不要開啟</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbRuleType" xml:space="preserve">
|
||||||
|
<value>规则类型</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbRuleTypeTips" xml:space="preserve">
|
||||||
|
<value>可对 Routing 和 DNS 单独设定规则,ALL 则都生效</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
|
@ -1,4 +1,4 @@
|
||||||
{
|
{
|
||||||
"domain": [
|
"domain": [
|
||||||
"amobile.music.tc.qq.com",
|
"amobile.music.tc.qq.com",
|
||||||
"api-jooxtt.sanook.com",
|
"api-jooxtt.sanook.com",
|
||||||
|
@ -28,6 +28,7 @@
|
||||||
"swdownload.apple.com",
|
"swdownload.apple.com",
|
||||||
"swquery.apple.com",
|
"swquery.apple.com",
|
||||||
"swscan.apple.com",
|
"swscan.apple.com",
|
||||||
|
"turn.cloudflare.com",
|
||||||
"trackercdn.kugou.com",
|
"trackercdn.kugou.com",
|
||||||
"xnotify.xboxlive.com"
|
"xnotify.xboxlive.com"
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.NetworkInformation;
|
using System.Net.NetworkInformation;
|
||||||
|
using ServiceLib.Common;
|
||||||
|
|
||||||
namespace ServiceLib.Services.CoreConfig;
|
namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
|
@ -16,7 +17,7 @@ public partial class CoreConfigSingboxService(Config config)
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (node == null
|
if (node == null
|
||||||
|| node.Port <= 0)
|
|| !node.IsValid())
|
||||||
{
|
{
|
||||||
ret.Msg = ResUI.CheckServerSettings;
|
ret.Msg = ResUI.CheckServerSettings;
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -29,6 +30,18 @@ public partial class CoreConfigSingboxService(Config config)
|
||||||
|
|
||||||
ret.Msg = ResUI.InitialConfiguration;
|
ret.Msg = ResUI.InitialConfiguration;
|
||||||
|
|
||||||
|
if (node.ConfigType.IsGroupType())
|
||||||
|
{
|
||||||
|
switch (node.ConfigType)
|
||||||
|
{
|
||||||
|
case EConfigType.PolicyGroup:
|
||||||
|
return await GenerateClientMultipleLoadConfig(node);
|
||||||
|
|
||||||
|
case EConfigType.ProxyChain:
|
||||||
|
return await GenerateClientChainConfig(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var result = EmbedUtils.GetEmbedText(Global.SingboxSampleClient);
|
var result = EmbedUtils.GetEmbedText(Global.SingboxSampleClient);
|
||||||
if (result.IsNullOrEmpty())
|
if (result.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
|
@ -142,12 +155,9 @@ public partial class CoreConfigSingboxService(Config config)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var item = await AppManager.Instance.GetProfileItem(it.IndexId);
|
var item = await AppManager.Instance.GetProfileItem(it.IndexId);
|
||||||
if (it.ConfigType is EConfigType.VMess or EConfigType.VLESS)
|
if (item is null || item.IsComplex() || !item.IsValid())
|
||||||
{
|
{
|
||||||
if (item is null || item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id))
|
continue;
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//find unused port
|
//find unused port
|
||||||
|
@ -187,27 +197,6 @@ public partial class CoreConfigSingboxService(Config config)
|
||||||
singboxConfig.inbounds.Add(inbound);
|
singboxConfig.inbounds.Add(inbound);
|
||||||
|
|
||||||
//outbound
|
//outbound
|
||||||
if (item is null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (item.ConfigType == EConfigType.Shadowsocks
|
|
||||||
&& !Global.SsSecuritiesInSingbox.Contains(item.Security))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (item.ConfigType == EConfigType.VLESS
|
|
||||||
&& !Global.Flows.Contains(item.Flow))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (it.ConfigType is EConfigType.VLESS or EConfigType.Trojan
|
|
||||||
&& item.StreamSecurity == Global.StreamSecurityReality
|
|
||||||
&& item.PublicKey.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var server = await GenServer(item);
|
var server = await GenServer(item);
|
||||||
if (server is null)
|
if (server is null)
|
||||||
{
|
{
|
||||||
|
@ -246,7 +235,7 @@ public partial class CoreConfigSingboxService(Config config)
|
||||||
}
|
}
|
||||||
singboxConfig.route.default_domain_resolver = new()
|
singboxConfig.route.default_domain_resolver = new()
|
||||||
{
|
{
|
||||||
server = Global.SingboxFinalResolverTag
|
server = Global.SingboxLocalDNSTag,
|
||||||
};
|
};
|
||||||
|
|
||||||
ret.Success = true;
|
ret.Success = true;
|
||||||
|
@ -266,7 +255,8 @@ public partial class CoreConfigSingboxService(Config config)
|
||||||
var ret = new RetResult();
|
var ret = new RetResult();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (node is not { Port: > 0 })
|
if (node == null
|
||||||
|
|| !node.IsValid())
|
||||||
{
|
{
|
||||||
ret.Msg = ResUI.CheckServerSettings;
|
ret.Msg = ResUI.CheckServerSettings;
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -318,7 +308,7 @@ public partial class CoreConfigSingboxService(Config config)
|
||||||
}
|
}
|
||||||
singboxConfig.route.default_domain_resolver = new()
|
singboxConfig.route.default_domain_resolver = new()
|
||||||
{
|
{
|
||||||
server = Global.SingboxFinalResolverTag
|
server = Global.SingboxLocalDNSTag,
|
||||||
};
|
};
|
||||||
|
|
||||||
singboxConfig.route.rules.Clear();
|
singboxConfig.route.rules.Clear();
|
||||||
|
@ -344,7 +334,7 @@ public partial class CoreConfigSingboxService(Config config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<RetResult> GenerateClientMultipleLoadConfig(List<ProfileItem> selecteds)
|
public async Task<RetResult> GenerateClientMultipleLoadConfig(ProfileItem parentNode)
|
||||||
{
|
{
|
||||||
var ret = new RetResult();
|
var ret = new RetResult();
|
||||||
try
|
try
|
||||||
|
@ -371,56 +361,77 @@ public partial class CoreConfigSingboxService(Config config)
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
singboxConfig.outbounds.RemoveAt(0);
|
||||||
|
|
||||||
await GenLog(singboxConfig);
|
await GenLog(singboxConfig);
|
||||||
await GenInbounds(singboxConfig);
|
await GenInbounds(singboxConfig);
|
||||||
await GenRouting(singboxConfig);
|
|
||||||
await GenExperimental(singboxConfig);
|
|
||||||
singboxConfig.outbounds.RemoveAt(0);
|
|
||||||
|
|
||||||
var proxyProfiles = new List<ProfileItem>();
|
var groupRet = await GenGroupOutbound(parentNode, singboxConfig);
|
||||||
foreach (var it in selecteds)
|
if (groupRet != 0)
|
||||||
{
|
|
||||||
if (!Global.SingboxSupportConfigType.Contains(it.ConfigType))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (it.Port <= 0)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
var item = await AppManager.Instance.GetProfileItem(it.IndexId);
|
|
||||||
if (item is null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (it.ConfigType is EConfigType.VMess or EConfigType.VLESS)
|
|
||||||
{
|
|
||||||
if (item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (item.ConfigType == EConfigType.Shadowsocks
|
|
||||||
&& !Global.SsSecuritiesInSingbox.Contains(item.Security))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (item.ConfigType == EConfigType.VLESS && !Global.Flows.Contains(item.Flow))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
//outbound
|
|
||||||
proxyProfiles.Add(item);
|
|
||||||
}
|
|
||||||
if (proxyProfiles.Count <= 0)
|
|
||||||
{
|
{
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
await GenOutboundsList(proxyProfiles, singboxConfig);
|
|
||||||
|
|
||||||
|
await GenRouting(singboxConfig);
|
||||||
|
await GenExperimental(singboxConfig);
|
||||||
|
await GenDns(null, singboxConfig);
|
||||||
|
await ConvertGeo2Ruleset(singboxConfig);
|
||||||
|
|
||||||
|
ret.Success = true;
|
||||||
|
|
||||||
|
ret.Data = await ApplyFullConfigTemplate(singboxConfig);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logging.SaveLog(_tag, ex);
|
||||||
|
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<RetResult> GenerateClientChainConfig(ProfileItem parentNode)
|
||||||
|
{
|
||||||
|
var ret = new RetResult();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_config == null)
|
||||||
|
{
|
||||||
|
ret.Msg = ResUI.CheckServerSettings;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.Msg = ResUI.InitialConfiguration;
|
||||||
|
|
||||||
|
var result = EmbedUtils.GetEmbedText(Global.SingboxSampleClient);
|
||||||
|
var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound);
|
||||||
|
if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
ret.Msg = ResUI.FailedGetDefaultConfiguration;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
var singboxConfig = JsonUtils.Deserialize<SingboxConfig>(result);
|
||||||
|
if (singboxConfig == null)
|
||||||
|
{
|
||||||
|
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
singboxConfig.outbounds.RemoveAt(0);
|
||||||
|
|
||||||
|
await GenLog(singboxConfig);
|
||||||
|
await GenInbounds(singboxConfig);
|
||||||
|
|
||||||
|
var groupRet = await GenGroupOutbound(parentNode, singboxConfig);
|
||||||
|
if (groupRet != 0)
|
||||||
|
{
|
||||||
|
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
await GenRouting(singboxConfig);
|
||||||
|
await GenExperimental(singboxConfig);
|
||||||
await GenDns(null, singboxConfig);
|
await GenDns(null, singboxConfig);
|
||||||
await ConvertGeo2Ruleset(singboxConfig);
|
await ConvertGeo2Ruleset(singboxConfig);
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ public partial class CoreConfigSingboxService
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await GenOutboundDnsRule(node, singboxConfig, Global.SingboxOutboundResolverTag);
|
await GenOutboundDnsRule(node, singboxConfig);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -58,16 +58,12 @@ public partial class CoreConfigSingboxService
|
||||||
|
|
||||||
var directDns = ParseDnsAddress(simpleDNSItem.DirectDNS);
|
var directDns = ParseDnsAddress(simpleDNSItem.DirectDNS);
|
||||||
directDns.tag = Global.SingboxDirectDNSTag;
|
directDns.tag = Global.SingboxDirectDNSTag;
|
||||||
directDns.domain_resolver = Global.SingboxFinalResolverTag;
|
directDns.domain_resolver = Global.SingboxLocalDNSTag;
|
||||||
|
|
||||||
var remoteDns = ParseDnsAddress(simpleDNSItem.RemoteDNS);
|
var remoteDns = ParseDnsAddress(simpleDNSItem.RemoteDNS);
|
||||||
remoteDns.tag = Global.SingboxRemoteDNSTag;
|
remoteDns.tag = Global.SingboxRemoteDNSTag;
|
||||||
remoteDns.detour = Global.ProxyTag;
|
remoteDns.detour = Global.ProxyTag;
|
||||||
remoteDns.domain_resolver = Global.SingboxFinalResolverTag;
|
remoteDns.domain_resolver = Global.SingboxLocalDNSTag;
|
||||||
|
|
||||||
var resolverDns = ParseDnsAddress(simpleDNSItem.SingboxOutboundsResolveDNS);
|
|
||||||
resolverDns.tag = Global.SingboxOutboundResolverTag;
|
|
||||||
resolverDns.domain_resolver = Global.SingboxFinalResolverTag;
|
|
||||||
|
|
||||||
var hostsDns = new Server4Sbox
|
var hostsDns = new Server4Sbox
|
||||||
{
|
{
|
||||||
|
@ -112,10 +108,6 @@ public partial class CoreConfigSingboxService
|
||||||
{
|
{
|
||||||
remoteDns.domain_resolver = Global.SingboxHostsDNSTag;
|
remoteDns.domain_resolver = Global.SingboxHostsDNSTag;
|
||||||
}
|
}
|
||||||
if (resolverDns.server == host.Key)
|
|
||||||
{
|
|
||||||
resolverDns.domain_resolver = Global.SingboxHostsDNSTag;
|
|
||||||
}
|
|
||||||
if (directDns.server == host.Key)
|
if (directDns.server == host.Key)
|
||||||
{
|
{
|
||||||
directDns.domain_resolver = Global.SingboxHostsDNSTag;
|
directDns.domain_resolver = Global.SingboxHostsDNSTag;
|
||||||
|
@ -126,7 +118,6 @@ public partial class CoreConfigSingboxService
|
||||||
singboxConfig.dns.servers ??= new List<Server4Sbox>();
|
singboxConfig.dns.servers ??= new List<Server4Sbox>();
|
||||||
singboxConfig.dns.servers.Add(remoteDns);
|
singboxConfig.dns.servers.Add(remoteDns);
|
||||||
singboxConfig.dns.servers.Add(directDns);
|
singboxConfig.dns.servers.Add(directDns);
|
||||||
singboxConfig.dns.servers.Add(resolverDns);
|
|
||||||
singboxConfig.dns.servers.Add(hostsDns);
|
singboxConfig.dns.servers.Add(hostsDns);
|
||||||
|
|
||||||
// fake ip
|
// fake ip
|
||||||
|
@ -147,8 +138,13 @@ public partial class CoreConfigSingboxService
|
||||||
|
|
||||||
private async Task<Server4Sbox> GenDnsDomains(SingboxConfig singboxConfig, SimpleDNSItem? simpleDNSItem)
|
private async Task<Server4Sbox> GenDnsDomains(SingboxConfig singboxConfig, SimpleDNSItem? simpleDNSItem)
|
||||||
{
|
{
|
||||||
var finalDns = ParseDnsAddress(simpleDNSItem.SingboxFinalResolveDNS);
|
var finalDnsAddress = "local";
|
||||||
finalDns.tag = Global.SingboxFinalResolverTag;
|
if (_config.TunModeItem.EnableTun)
|
||||||
|
{
|
||||||
|
finalDnsAddress = "dhcp://auto";
|
||||||
|
}
|
||||||
|
var finalDns = ParseDnsAddress(finalDnsAddress);
|
||||||
|
finalDns.tag = Global.SingboxLocalDNSTag;
|
||||||
singboxConfig.dns ??= new Dns4Sbox();
|
singboxConfig.dns ??= new Dns4Sbox();
|
||||||
singboxConfig.dns.servers ??= new List<Server4Sbox>();
|
singboxConfig.dns.servers ??= new List<Server4Sbox>();
|
||||||
singboxConfig.dns.servers.Add(finalDns);
|
singboxConfig.dns.servers.Add(finalDns);
|
||||||
|
@ -253,6 +249,11 @@ public partial class CoreConfigSingboxService
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (item.RuleType == ERuleType.Routing)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var rule = new Rule4Sbox();
|
var rule = new Rule4Sbox();
|
||||||
var validDomains = item.Domain.Count(it => ParseV2Domain(it, rule));
|
var validDomains = item.Domain.Count(it => ParseV2Domain(it, rule));
|
||||||
if (validDomains <= 0)
|
if (validDomains <= 0)
|
||||||
|
@ -337,7 +338,7 @@ public partial class CoreConfigSingboxService
|
||||||
await GenDnsDomainsLegacyCompatible(singboxConfig, item);
|
await GenDnsDomainsLegacyCompatible(singboxConfig, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
await GenOutboundDnsRule(node, singboxConfig, Global.SingboxFinalResolverTag);
|
await GenOutboundDnsRule(node, singboxConfig);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -346,16 +347,17 @@ public partial class CoreConfigSingboxService
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenDnsDomainsCompatible(SingboxConfig singboxConfig, DNSItem? dNSItem)
|
private async Task<int> GenDnsDomainsCompatible(SingboxConfig singboxConfig, DNSItem? dnsItem)
|
||||||
{
|
{
|
||||||
var dns4Sbox = singboxConfig.dns ?? new();
|
var dns4Sbox = singboxConfig.dns ?? new();
|
||||||
dns4Sbox.servers ??= [];
|
dns4Sbox.servers ??= [];
|
||||||
dns4Sbox.rules ??= [];
|
dns4Sbox.rules ??= [];
|
||||||
|
|
||||||
var tag = Global.SingboxFinalResolverTag;
|
var tag = Global.SingboxLocalDNSTag;
|
||||||
var localDnsAddress = string.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress;
|
|
||||||
|
|
||||||
var localDnsServer = ParseDnsAddress(localDnsAddress);
|
var finalDnsAddress = string.IsNullOrEmpty(dnsItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dnsItem?.DomainDNSAddress;
|
||||||
|
|
||||||
|
var localDnsServer = ParseDnsAddress(finalDnsAddress);
|
||||||
localDnsServer.tag = tag;
|
localDnsServer.tag = tag;
|
||||||
|
|
||||||
dns4Sbox.servers.Add(localDnsServer);
|
dns4Sbox.servers.Add(localDnsServer);
|
||||||
|
@ -364,19 +366,19 @@ public partial class CoreConfigSingboxService
|
||||||
return await Task.FromResult(0);
|
return await Task.FromResult(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenDnsDomainsLegacyCompatible(SingboxConfig singboxConfig, DNSItem? dNSItem)
|
private async Task<int> GenDnsDomainsLegacyCompatible(SingboxConfig singboxConfig, DNSItem? dnsItem)
|
||||||
{
|
{
|
||||||
var dns4Sbox = singboxConfig.dns ?? new();
|
var dns4Sbox = singboxConfig.dns ?? new();
|
||||||
dns4Sbox.servers ??= [];
|
dns4Sbox.servers ??= [];
|
||||||
dns4Sbox.rules ??= [];
|
dns4Sbox.rules ??= [];
|
||||||
|
|
||||||
var tag = Global.SingboxFinalResolverTag;
|
var tag = Global.SingboxLocalDNSTag;
|
||||||
dns4Sbox.servers.Add(new()
|
dns4Sbox.servers.Add(new()
|
||||||
{
|
{
|
||||||
tag = tag,
|
tag = tag,
|
||||||
address = string.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress,
|
address = string.IsNullOrEmpty(dnsItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dnsItem?.DomainDNSAddress,
|
||||||
detour = Global.DirectTag,
|
detour = Global.DirectTag,
|
||||||
strategy = string.IsNullOrEmpty(dNSItem?.DomainStrategy4Freedom) ? null : dNSItem?.DomainStrategy4Freedom,
|
strategy = string.IsNullOrEmpty(dnsItem?.DomainStrategy4Freedom) ? null : dnsItem?.DomainStrategy4Freedom,
|
||||||
});
|
});
|
||||||
dns4Sbox.rules.Insert(0, new()
|
dns4Sbox.rules.Insert(0, new()
|
||||||
{
|
{
|
||||||
|
@ -407,23 +409,26 @@ public partial class CoreConfigSingboxService
|
||||||
return await Task.FromResult(0);
|
return await Task.FromResult(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenOutboundDnsRule(ProfileItem? node, SingboxConfig singboxConfig, string? server)
|
private async Task<int> GenOutboundDnsRule(ProfileItem? node, SingboxConfig singboxConfig)
|
||||||
{
|
{
|
||||||
if (node == null)
|
if (node == null)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
var domain = string.Empty;
|
List<string> domain = new();
|
||||||
if (Utils.IsDomain(node.Address)) // normal outbound
|
if (Utils.IsDomain(node.Address)) // normal outbound
|
||||||
{
|
{
|
||||||
domain = node.Address;
|
domain.Add(node.Address);
|
||||||
}
|
}
|
||||||
else if (node.Address == Global.Loopback && node.SpiderX.IsNotEmpty() && Utils.IsDomain(node.SpiderX)) // Tun2SocksAddress
|
if (node.Address == Global.Loopback && node.SpiderX.IsNotEmpty()) // Tun2SocksAddress
|
||||||
{
|
{
|
||||||
domain = node.SpiderX;
|
domain.AddRange(Utils.String2List(node.SpiderX)
|
||||||
|
.Where(Utils.IsDomain)
|
||||||
|
.Distinct()
|
||||||
|
.ToList());
|
||||||
}
|
}
|
||||||
if (domain.IsNullOrEmpty())
|
if (domain.Count == 0)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -431,8 +436,8 @@ public partial class CoreConfigSingboxService
|
||||||
singboxConfig.dns.rules ??= new List<Rule4Sbox>();
|
singboxConfig.dns.rules ??= new List<Rule4Sbox>();
|
||||||
singboxConfig.dns.rules.Insert(0, new Rule4Sbox
|
singboxConfig.dns.rules.Insert(0, new Rule4Sbox
|
||||||
{
|
{
|
||||||
server = server,
|
server = Global.SingboxLocalDNSTag,
|
||||||
domain = [domain],
|
domain = domain,
|
||||||
});
|
});
|
||||||
|
|
||||||
return await Task.FromResult(0);
|
return await Task.FromResult(0);
|
||||||
|
|
|
@ -179,13 +179,21 @@ public partial class CoreConfigSingboxService
|
||||||
if (node.ConfigType == EConfigType.WireGuard)
|
if (node.ConfigType == EConfigType.WireGuard)
|
||||||
{
|
{
|
||||||
var endpoint = JsonUtils.Deserialize<Endpoints4Sbox>(txtOutbound);
|
var endpoint = JsonUtils.Deserialize<Endpoints4Sbox>(txtOutbound);
|
||||||
await GenEndpoint(node, endpoint);
|
var ret = await GenEndpoint(node, endpoint);
|
||||||
|
if (ret != 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return endpoint;
|
return endpoint;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var outbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
|
var outbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
|
||||||
await GenOutbound(node, outbound);
|
var ret = await GenOutbound(node, outbound);
|
||||||
|
if (ret != 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return outbound;
|
return outbound;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -196,6 +204,54 @@ public partial class CoreConfigSingboxService
|
||||||
return await Task.FromResult<BaseServer4Sbox?>(null);
|
return await Task.FromResult<BaseServer4Sbox?>(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<int> GenGroupOutbound(ProfileItem node, SingboxConfig singboxConfig, string baseTagName = Global.ProxyTag, bool ignoreOriginChain = false)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!node.ConfigType.IsGroupType())
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
var hasCycle = ProfileGroupItemManager.HasCycle(node.IndexId);
|
||||||
|
if (hasCycle)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var (childProfiles, profileGroupItem) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId);
|
||||||
|
if (childProfiles.Count <= 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
switch (node.ConfigType)
|
||||||
|
{
|
||||||
|
case EConfigType.PolicyGroup:
|
||||||
|
if (ignoreOriginChain)
|
||||||
|
{
|
||||||
|
await GenOutboundsList(childProfiles, singboxConfig, profileGroupItem.MultipleLoad, baseTagName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await GenOutboundsListWithChain(childProfiles, singboxConfig, profileGroupItem.MultipleLoad, baseTagName);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EConfigType.ProxyChain:
|
||||||
|
await GenChainOutboundsList(childProfiles, singboxConfig, baseTagName);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logging.SaveLog(_tag, ex);
|
||||||
|
}
|
||||||
|
return await Task.FromResult(0);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<int> GenOutboundMux(ProfileItem node, Outbound4Sbox outbound)
|
private async Task<int> GenOutboundMux(ProfileItem node, Outbound4Sbox outbound)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -410,7 +466,7 @@ public partial class CoreConfigSingboxService
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenOutboundsList(List<ProfileItem> nodes, SingboxConfig singboxConfig)
|
private async Task<int> GenOutboundsListWithChain(List<ProfileItem> nodes, SingboxConfig singboxConfig, EMultipleLoad multipleLoad, string baseTagName = Global.ProxyTag)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -438,6 +494,29 @@ public partial class CoreConfigSingboxService
|
||||||
{
|
{
|
||||||
index++;
|
index++;
|
||||||
|
|
||||||
|
if (node.ConfigType.IsGroupType())
|
||||||
|
{
|
||||||
|
var (childProfiles, profileGroupItem) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId);
|
||||||
|
if (childProfiles.Count <= 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var childBaseTagName = $"{baseTagName}-{index}";
|
||||||
|
var ret = node.ConfigType switch
|
||||||
|
{
|
||||||
|
EConfigType.PolicyGroup =>
|
||||||
|
await GenOutboundsListWithChain(childProfiles, singboxConfig, profileGroupItem.MultipleLoad, childBaseTagName),
|
||||||
|
EConfigType.ProxyChain =>
|
||||||
|
await GenChainOutboundsList(childProfiles, singboxConfig, childBaseTagName),
|
||||||
|
_ => throw new NotImplementedException()
|
||||||
|
};
|
||||||
|
if (ret == 0)
|
||||||
|
{
|
||||||
|
proxyTags.Add(childBaseTagName);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Handle proxy chain
|
// Handle proxy chain
|
||||||
string? prevTag = null;
|
string? prevTag = null;
|
||||||
var currentServer = await GenServer(node);
|
var currentServer = await GenServer(node);
|
||||||
|
@ -450,7 +529,7 @@ public partial class CoreConfigSingboxService
|
||||||
var subItem = await AppManager.Instance.GetSubItem(node.Subid);
|
var subItem = await AppManager.Instance.GetSubItem(node.Subid);
|
||||||
|
|
||||||
// current proxy
|
// current proxy
|
||||||
currentServer.tag = $"{Global.ProxyTag}-{index}";
|
currentServer.tag = $"{baseTagName}-{index}";
|
||||||
proxyTags.Add(currentServer.tag);
|
proxyTags.Add(currentServer.tag);
|
||||||
|
|
||||||
if (!node.Subid.IsNullOrEmpty())
|
if (!node.Subid.IsNullOrEmpty())
|
||||||
|
@ -467,7 +546,7 @@ public partial class CoreConfigSingboxService
|
||||||
{
|
{
|
||||||
var prevOutbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
|
var prevOutbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
|
||||||
await GenOutbound(prevNode, prevOutbound);
|
await GenOutbound(prevNode, prevOutbound);
|
||||||
prevTag = $"prev-{Global.ProxyTag}-{++prevIndex}";
|
prevTag = $"prev-{baseTagName}-{++prevIndex}";
|
||||||
prevOutbound.tag = prevTag;
|
prevOutbound.tag = prevTag;
|
||||||
prevOutbounds.Add(prevOutbound);
|
prevOutbounds.Add(prevOutbound);
|
||||||
}
|
}
|
||||||
|
@ -508,16 +587,21 @@ public partial class CoreConfigSingboxService
|
||||||
var outUrltest = new Outbound4Sbox
|
var outUrltest = new Outbound4Sbox
|
||||||
{
|
{
|
||||||
type = "urltest",
|
type = "urltest",
|
||||||
tag = $"{Global.ProxyTag}-auto",
|
tag = $"{baseTagName}-auto",
|
||||||
outbounds = proxyTags,
|
outbounds = proxyTags,
|
||||||
interrupt_exist_connections = false,
|
interrupt_exist_connections = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (multipleLoad == EMultipleLoad.Fallback)
|
||||||
|
{
|
||||||
|
outUrltest.tolerance = 5000;
|
||||||
|
}
|
||||||
|
|
||||||
// Add selector outbound (manual selection)
|
// Add selector outbound (manual selection)
|
||||||
var outSelector = new Outbound4Sbox
|
var outSelector = new Outbound4Sbox
|
||||||
{
|
{
|
||||||
type = "selector",
|
type = "selector",
|
||||||
tag = Global.ProxyTag,
|
tag = baseTagName,
|
||||||
outbounds = JsonUtils.DeepCopy(proxyTags),
|
outbounds = JsonUtils.DeepCopy(proxyTags),
|
||||||
interrupt_exist_connections = false,
|
interrupt_exist_connections = false,
|
||||||
};
|
};
|
||||||
|
@ -529,12 +613,12 @@ public partial class CoreConfigSingboxService
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge results: first the selector/urltest/proxies, then other outbounds, and finally prev outbounds
|
// Merge results: first the selector/urltest/proxies, then other outbounds, and finally prev outbounds
|
||||||
resultOutbounds.AddRange(prevOutbounds);
|
var serverList = new List<BaseServer4Sbox>();
|
||||||
resultOutbounds.AddRange(singboxConfig.outbounds);
|
serverList = serverList.Concat(prevOutbounds)
|
||||||
singboxConfig.outbounds = resultOutbounds;
|
.Concat(resultOutbounds)
|
||||||
singboxConfig.endpoints ??= new List<Endpoints4Sbox>();
|
.Concat(resultEndpoints)
|
||||||
resultEndpoints.AddRange(singboxConfig.endpoints);
|
.ToList();
|
||||||
singboxConfig.endpoints = resultEndpoints;
|
await AddRangeOutbounds(serverList, singboxConfig, baseTagName == Global.ProxyTag);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -574,4 +658,163 @@ public partial class CoreConfigSingboxService
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<int> GenOutboundsList(List<ProfileItem> nodes, SingboxConfig singboxConfig, EMultipleLoad multipleLoad, string baseTagName = Global.ProxyTag)
|
||||||
|
{
|
||||||
|
var resultOutbounds = new List<Outbound4Sbox>();
|
||||||
|
var resultEndpoints = new List<Endpoints4Sbox>(); // For endpoints
|
||||||
|
var proxyTags = new List<string>(); // For selector and urltest outbounds
|
||||||
|
for (var i = 0; i < nodes.Count; i++)
|
||||||
|
{
|
||||||
|
var node = nodes[i];
|
||||||
|
if (node == null)
|
||||||
|
continue;
|
||||||
|
if (node.ConfigType.IsGroupType())
|
||||||
|
{
|
||||||
|
var (childProfiles, profileGroupItem) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId);
|
||||||
|
if (childProfiles.Count <= 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var childBaseTagName = $"{baseTagName}-{i + 1}";
|
||||||
|
var ret = node.ConfigType switch
|
||||||
|
{
|
||||||
|
EConfigType.PolicyGroup =>
|
||||||
|
await GenOutboundsList(childProfiles, singboxConfig, profileGroupItem.MultipleLoad, childBaseTagName),
|
||||||
|
EConfigType.ProxyChain =>
|
||||||
|
await GenChainOutboundsList(childProfiles, singboxConfig, childBaseTagName),
|
||||||
|
_ => throw new NotImplementedException()
|
||||||
|
};
|
||||||
|
if (ret == 0)
|
||||||
|
{
|
||||||
|
proxyTags.Add(childBaseTagName);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var server = await GenServer(node);
|
||||||
|
if (server is null)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
server.tag = baseTagName + (i + 1).ToString();
|
||||||
|
if (server is Endpoints4Sbox endpoint)
|
||||||
|
{
|
||||||
|
resultEndpoints.Add(endpoint);
|
||||||
|
}
|
||||||
|
else if (server is Outbound4Sbox outbound)
|
||||||
|
{
|
||||||
|
resultOutbounds.Add(outbound);
|
||||||
|
}
|
||||||
|
proxyTags.Add(server.tag);
|
||||||
|
}
|
||||||
|
// Add urltest outbound (auto selection based on latency)
|
||||||
|
if (proxyTags.Count > 0)
|
||||||
|
{
|
||||||
|
var outUrltest = new Outbound4Sbox
|
||||||
|
{
|
||||||
|
type = "urltest",
|
||||||
|
tag = $"{baseTagName}-auto",
|
||||||
|
outbounds = proxyTags,
|
||||||
|
interrupt_exist_connections = false,
|
||||||
|
};
|
||||||
|
if (multipleLoad == EMultipleLoad.Fallback)
|
||||||
|
{
|
||||||
|
outUrltest.tolerance = 5000;
|
||||||
|
}
|
||||||
|
// Add selector outbound (manual selection)
|
||||||
|
var outSelector = new Outbound4Sbox
|
||||||
|
{
|
||||||
|
type = "selector",
|
||||||
|
tag = baseTagName,
|
||||||
|
outbounds = JsonUtils.DeepCopy(proxyTags),
|
||||||
|
interrupt_exist_connections = false,
|
||||||
|
};
|
||||||
|
outSelector.outbounds.Insert(0, outUrltest.tag);
|
||||||
|
// Insert these at the beginning
|
||||||
|
resultOutbounds.Insert(0, outUrltest);
|
||||||
|
resultOutbounds.Insert(0, outSelector);
|
||||||
|
}
|
||||||
|
var serverList = new List<BaseServer4Sbox>();
|
||||||
|
serverList = serverList.Concat(resultOutbounds)
|
||||||
|
.Concat(resultEndpoints)
|
||||||
|
.ToList();
|
||||||
|
await AddRangeOutbounds(serverList, singboxConfig, baseTagName == Global.ProxyTag);
|
||||||
|
return await Task.FromResult(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<int> GenChainOutboundsList(List<ProfileItem> nodes, SingboxConfig singboxConfig, string baseTagName = Global.ProxyTag)
|
||||||
|
{
|
||||||
|
// Based on actual network flow instead of data packets
|
||||||
|
var nodesReverse = nodes.AsEnumerable().Reverse().ToList();
|
||||||
|
var resultOutbounds = new List<Outbound4Sbox>();
|
||||||
|
var resultEndpoints = new List<Endpoints4Sbox>(); // For endpoints
|
||||||
|
for (var i = 0; i < nodesReverse.Count; i++)
|
||||||
|
{
|
||||||
|
var node = nodesReverse[i];
|
||||||
|
var server = await GenServer(node);
|
||||||
|
|
||||||
|
if (server is null)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == 0)
|
||||||
|
{
|
||||||
|
server.tag = baseTagName;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
server.tag = baseTagName + i.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i != nodesReverse.Count - 1)
|
||||||
|
{
|
||||||
|
server.detour = baseTagName + (i + 1).ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (server is Endpoints4Sbox endpoint)
|
||||||
|
{
|
||||||
|
resultEndpoints.Add(endpoint);
|
||||||
|
}
|
||||||
|
else if (server is Outbound4Sbox outbound)
|
||||||
|
{
|
||||||
|
resultOutbounds.Add(outbound);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var serverList = new List<BaseServer4Sbox>();
|
||||||
|
serverList = serverList.Concat(resultOutbounds)
|
||||||
|
.Concat(resultEndpoints)
|
||||||
|
.ToList();
|
||||||
|
await AddRangeOutbounds(serverList, singboxConfig, baseTagName == Global.ProxyTag);
|
||||||
|
return await Task.FromResult(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<int> AddRangeOutbounds(List<BaseServer4Sbox> servers, SingboxConfig singboxConfig, bool prepend = true)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (servers is null || servers.Count <= 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
var outbounds = servers.Where(s => s is Outbound4Sbox).Cast<Outbound4Sbox>().ToList();
|
||||||
|
var endpoints = servers.Where(s => s is Endpoints4Sbox).Cast<Endpoints4Sbox>().ToList();
|
||||||
|
singboxConfig.endpoints ??= new();
|
||||||
|
if (prepend)
|
||||||
|
{
|
||||||
|
singboxConfig.outbounds.InsertRange(0, outbounds);
|
||||||
|
singboxConfig.endpoints.InsertRange(0, endpoints);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
singboxConfig.outbounds.AddRange(outbounds);
|
||||||
|
singboxConfig.endpoints.AddRange(endpoints);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logging.SaveLog(_tag, ex);
|
||||||
|
}
|
||||||
|
return await Task.FromResult(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,13 +9,13 @@ public partial class CoreConfigSingboxService
|
||||||
singboxConfig.route.final = Global.ProxyTag;
|
singboxConfig.route.final = Global.ProxyTag;
|
||||||
var item = _config.SimpleDNSItem;
|
var item = _config.SimpleDNSItem;
|
||||||
|
|
||||||
var defaultDomainResolverTag = Global.SingboxOutboundResolverTag;
|
var defaultDomainResolverTag = Global.SingboxDirectDNSTag;
|
||||||
var directDNSStrategy = item.SingboxStrategy4Direct.IsNullOrEmpty() ? Global.SingboxDomainStrategy4Out.FirstOrDefault() : item.SingboxStrategy4Direct;
|
var directDNSStrategy = item.SingboxStrategy4Direct.IsNullOrEmpty() ? Global.SingboxDomainStrategy4Out.FirstOrDefault() : item.SingboxStrategy4Direct;
|
||||||
|
|
||||||
var rawDNSItem = await AppManager.Instance.GetDNSItem(ECoreType.sing_box);
|
var rawDNSItem = await AppManager.Instance.GetDNSItem(ECoreType.sing_box);
|
||||||
if (rawDNSItem != null && rawDNSItem.Enabled == true)
|
if (rawDNSItem != null && rawDNSItem.Enabled == true)
|
||||||
{
|
{
|
||||||
defaultDomainResolverTag = Global.SingboxFinalResolverTag;
|
defaultDomainResolverTag = Global.SingboxLocalDNSTag;
|
||||||
directDNSStrategy = rawDNSItem.DomainStrategy4Freedom.IsNullOrEmpty() ? Global.SingboxDomainStrategy4Out.FirstOrDefault() : rawDNSItem.DomainStrategy4Freedom;
|
directDNSStrategy = rawDNSItem.DomainStrategy4Freedom.IsNullOrEmpty() ? Global.SingboxDomainStrategy4Out.FirstOrDefault() : rawDNSItem.DomainStrategy4Freedom;
|
||||||
}
|
}
|
||||||
singboxConfig.route.default_domain_resolver = new()
|
singboxConfig.route.default_domain_resolver = new()
|
||||||
|
@ -136,13 +136,21 @@ public partial class CoreConfigSingboxService
|
||||||
var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet);
|
var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet);
|
||||||
foreach (var item1 in rules ?? [])
|
foreach (var item1 in rules ?? [])
|
||||||
{
|
{
|
||||||
if (item1.Enabled)
|
if (!item1.Enabled)
|
||||||
{
|
{
|
||||||
await GenRoutingUserRule(item1, singboxConfig);
|
continue;
|
||||||
if (item1.Ip != null && item1.Ip.Count > 0)
|
}
|
||||||
{
|
|
||||||
ipRules.Add(item1);
|
if (item1.RuleType == ERuleType.DNS)
|
||||||
}
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
await GenRoutingUserRule(item1, singboxConfig);
|
||||||
|
|
||||||
|
if (item1.Ip?.Count > 0)
|
||||||
|
{
|
||||||
|
ipRules.Add(item1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -368,26 +376,38 @@ public partial class CoreConfigSingboxService
|
||||||
}
|
}
|
||||||
|
|
||||||
var node = await AppManager.Instance.GetProfileItemViaRemarks(outboundTag);
|
var node = await AppManager.Instance.GetProfileItemViaRemarks(outboundTag);
|
||||||
|
|
||||||
if (node == null
|
if (node == null
|
||||||
|| !Global.SingboxSupportConfigType.Contains(node.ConfigType))
|
|| (!Global.SingboxSupportConfigType.Contains(node.ConfigType)
|
||||||
|
&& !node.ConfigType.IsGroupType()))
|
||||||
{
|
{
|
||||||
return Global.ProxyTag;
|
return Global.ProxyTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
var tag = Global.ProxyTag + node.IndexId.ToString();
|
var tag = $"{node.IndexId}-{Global.ProxyTag}";
|
||||||
if (singboxConfig.outbounds.Any(o => o.tag == tag)
|
if (singboxConfig.outbounds.Any(o => o.tag == tag)
|
||||||
|| (singboxConfig.endpoints != null && singboxConfig.endpoints.Any(e => e.tag == tag)))
|
|| (singboxConfig.endpoints != null && singboxConfig.endpoints.Any(e => e.tag == tag)))
|
||||||
{
|
{
|
||||||
return tag;
|
return tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (node.ConfigType.IsGroupType())
|
||||||
|
{
|
||||||
|
var ret = await GenGroupOutbound(node, singboxConfig, tag);
|
||||||
|
if (ret == 0)
|
||||||
|
{
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
return Global.ProxyTag;
|
||||||
|
}
|
||||||
|
|
||||||
var server = await GenServer(node);
|
var server = await GenServer(node);
|
||||||
if (server is null)
|
if (server is null)
|
||||||
{
|
{
|
||||||
return Global.ProxyTag;
|
return Global.ProxyTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
server.tag = Global.ProxyTag + node.IndexId.ToString();
|
server.tag = tag;
|
||||||
if (server is Endpoints4Sbox endpoint)
|
if (server is Endpoints4Sbox endpoint)
|
||||||
{
|
{
|
||||||
singboxConfig.endpoints ??= new();
|
singboxConfig.endpoints ??= new();
|
||||||
|
|
|
@ -16,7 +16,7 @@ public partial class CoreConfigV2rayService(Config config)
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (node == null
|
if (node == null
|
||||||
|| node.Port <= 0)
|
|| !node.IsValid())
|
||||||
{
|
{
|
||||||
ret.Msg = ResUI.CheckServerSettings;
|
ret.Msg = ResUI.CheckServerSettings;
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -30,6 +30,18 @@ public partial class CoreConfigV2rayService(Config config)
|
||||||
|
|
||||||
ret.Msg = ResUI.InitialConfiguration;
|
ret.Msg = ResUI.InitialConfiguration;
|
||||||
|
|
||||||
|
if (node.ConfigType.IsGroupType())
|
||||||
|
{
|
||||||
|
switch (node.ConfigType)
|
||||||
|
{
|
||||||
|
case EConfigType.PolicyGroup:
|
||||||
|
return await GenerateClientMultipleLoadConfig(node);
|
||||||
|
|
||||||
|
case EConfigType.ProxyChain:
|
||||||
|
return await GenerateClientChainConfig(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
|
var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
|
||||||
if (result.IsNullOrEmpty())
|
if (result.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
|
@ -71,7 +83,7 @@ public partial class CoreConfigV2rayService(Config config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<RetResult> GenerateClientMultipleLoadConfig(List<ProfileItem> selecteds, EMultipleLoad multipleLoad)
|
public async Task<RetResult> GenerateClientMultipleLoadConfig(ProfileItem parentNode)
|
||||||
{
|
{
|
||||||
var ret = new RetResult();
|
var ret = new RetResult();
|
||||||
|
|
||||||
|
@ -99,70 +111,50 @@ public partial class CoreConfigV2rayService(Config config)
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
v2rayConfig.outbounds.RemoveAt(0);
|
||||||
|
|
||||||
await GenLog(v2rayConfig);
|
await GenLog(v2rayConfig);
|
||||||
await GenInbounds(v2rayConfig);
|
await GenInbounds(v2rayConfig);
|
||||||
await GenRouting(v2rayConfig);
|
|
||||||
await GenDns(null, v2rayConfig);
|
|
||||||
await GenStatistic(v2rayConfig);
|
|
||||||
v2rayConfig.outbounds.RemoveAt(0);
|
|
||||||
|
|
||||||
var proxyProfiles = new List<ProfileItem>();
|
var groupRet = await GenGroupOutbound(parentNode, v2rayConfig);
|
||||||
foreach (var it in selecteds)
|
if (groupRet != 0)
|
||||||
{
|
|
||||||
if (!Global.XraySupportConfigType.Contains(it.ConfigType))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (it.Port <= 0)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
var item = await AppManager.Instance.GetProfileItem(it.IndexId);
|
|
||||||
if (item is null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (it.ConfigType is EConfigType.VMess or EConfigType.VLESS)
|
|
||||||
{
|
|
||||||
if (item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (item.ConfigType == EConfigType.Shadowsocks
|
|
||||||
&& !Global.SsSecuritiesInSingbox.Contains(item.Security))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (item.ConfigType == EConfigType.VLESS && !Global.Flows.Contains(item.Flow))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
//outbound
|
|
||||||
proxyProfiles.Add(item);
|
|
||||||
}
|
|
||||||
if (proxyProfiles.Count <= 0)
|
|
||||||
{
|
{
|
||||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
await GenOutboundsList(proxyProfiles, v2rayConfig);
|
|
||||||
|
|
||||||
//add balancers
|
await GenRouting(v2rayConfig);
|
||||||
await GenBalancer(v2rayConfig, multipleLoad);
|
await GenDns(null, v2rayConfig);
|
||||||
|
await GenStatistic(v2rayConfig);
|
||||||
|
|
||||||
var balancer = v2rayConfig.routing.balancers.First();
|
var defaultBalancerTag = $"{Global.ProxyTag}{Global.BalancerTagSuffix}";
|
||||||
|
|
||||||
//add rule
|
//add rule
|
||||||
var rules = v2rayConfig.routing.rules.Where(t => t.outboundTag == Global.ProxyTag).ToList();
|
var rules = v2rayConfig.routing.rules;
|
||||||
if (rules?.Count > 0)
|
if (rules?.Count > 0 && ((v2rayConfig.routing.balancers?.Count ?? 0) > 0))
|
||||||
{
|
{
|
||||||
|
var balancerTagSet = v2rayConfig.routing.balancers
|
||||||
|
.Select(b => b.tag)
|
||||||
|
.ToHashSet();
|
||||||
|
|
||||||
foreach (var rule in rules)
|
foreach (var rule in rules)
|
||||||
{
|
{
|
||||||
rule.outboundTag = null;
|
if (rule.outboundTag == null)
|
||||||
rule.balancerTag = balancer.tag;
|
continue;
|
||||||
|
|
||||||
|
if (balancerTagSet.Contains(rule.outboundTag))
|
||||||
|
{
|
||||||
|
rule.balancerTag = rule.outboundTag;
|
||||||
|
rule.outboundTag = null;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var outboundWithSuffix = rule.outboundTag + Global.BalancerTagSuffix;
|
||||||
|
if (balancerTagSet.Contains(outboundWithSuffix))
|
||||||
|
{
|
||||||
|
rule.balancerTag = outboundWithSuffix;
|
||||||
|
rule.outboundTag = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (v2rayConfig.routing.domainStrategy == Global.IPIfNonMatch)
|
if (v2rayConfig.routing.domainStrategy == Global.IPIfNonMatch)
|
||||||
|
@ -170,7 +162,7 @@ public partial class CoreConfigV2rayService(Config config)
|
||||||
v2rayConfig.routing.rules.Add(new()
|
v2rayConfig.routing.rules.Add(new()
|
||||||
{
|
{
|
||||||
ip = ["0.0.0.0/0", "::/0"],
|
ip = ["0.0.0.0/0", "::/0"],
|
||||||
balancerTag = balancer.tag,
|
balancerTag = defaultBalancerTag,
|
||||||
type = "field"
|
type = "field"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -179,14 +171,71 @@ public partial class CoreConfigV2rayService(Config config)
|
||||||
v2rayConfig.routing.rules.Add(new()
|
v2rayConfig.routing.rules.Add(new()
|
||||||
{
|
{
|
||||||
network = "tcp,udp",
|
network = "tcp,udp",
|
||||||
balancerTag = balancer.tag,
|
balancerTag = defaultBalancerTag,
|
||||||
type = "field"
|
type = "field"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ret.Success = true;
|
ret.Success = true;
|
||||||
|
|
||||||
ret.Data = await ApplyFullConfigTemplate(v2rayConfig, true);
|
ret.Data = await ApplyFullConfigTemplate(v2rayConfig);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logging.SaveLog(_tag, ex);
|
||||||
|
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<RetResult> GenerateClientChainConfig(ProfileItem parentNode)
|
||||||
|
{
|
||||||
|
var ret = new RetResult();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_config == null)
|
||||||
|
{
|
||||||
|
ret.Msg = ResUI.CheckServerSettings;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.Msg = ResUI.InitialConfiguration;
|
||||||
|
|
||||||
|
string result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
|
||||||
|
string txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
|
||||||
|
if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
ret.Msg = ResUI.FailedGetDefaultConfiguration;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
var v2rayConfig = JsonUtils.Deserialize<V2rayConfig>(result);
|
||||||
|
if (v2rayConfig == null)
|
||||||
|
{
|
||||||
|
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
v2rayConfig.outbounds.RemoveAt(0);
|
||||||
|
|
||||||
|
await GenLog(v2rayConfig);
|
||||||
|
await GenInbounds(v2rayConfig);
|
||||||
|
|
||||||
|
var groupRet = await GenGroupOutbound(parentNode, v2rayConfig);
|
||||||
|
if (groupRet != 0)
|
||||||
|
{
|
||||||
|
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
await GenRouting(v2rayConfig);
|
||||||
|
await GenDns(null, v2rayConfig);
|
||||||
|
await GenStatistic(v2rayConfig);
|
||||||
|
|
||||||
|
ret.Success = true;
|
||||||
|
|
||||||
|
ret.Data = await ApplyFullConfigTemplate(v2rayConfig);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
@ -255,12 +304,9 @@ public partial class CoreConfigV2rayService(Config config)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var item = await AppManager.Instance.GetProfileItem(it.IndexId);
|
var item = await AppManager.Instance.GetProfileItem(it.IndexId);
|
||||||
if (it.ConfigType is EConfigType.VMess or EConfigType.VLESS)
|
if (item is null || item.IsComplex() || !item.IsValid())
|
||||||
{
|
{
|
||||||
if (item is null || item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id))
|
continue;
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//find unused port
|
//find unused port
|
||||||
|
@ -289,28 +335,6 @@ public partial class CoreConfigV2rayService(Config config)
|
||||||
it.Port = port;
|
it.Port = port;
|
||||||
it.AllowTest = true;
|
it.AllowTest = true;
|
||||||
|
|
||||||
//outbound
|
|
||||||
if (item is null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (item.ConfigType == EConfigType.Shadowsocks
|
|
||||||
&& !Global.SsSecuritiesInXray.Contains(item.Security))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (item.ConfigType == EConfigType.VLESS
|
|
||||||
&& !Global.Flows.Contains(item.Flow))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (it.ConfigType is EConfigType.VLESS or EConfigType.Trojan
|
|
||||||
&& item.StreamSecurity == Global.StreamSecurityReality
|
|
||||||
&& item.PublicKey.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
//inbound
|
//inbound
|
||||||
Inbounds4Ray inbound = new()
|
Inbounds4Ray inbound = new()
|
||||||
{
|
{
|
||||||
|
@ -321,6 +345,7 @@ public partial class CoreConfigV2rayService(Config config)
|
||||||
inbound.tag = inbound.protocol + inbound.port.ToString();
|
inbound.tag = inbound.protocol + inbound.port.ToString();
|
||||||
v2rayConfig.inbounds.Add(inbound);
|
v2rayConfig.inbounds.Add(inbound);
|
||||||
|
|
||||||
|
//outbound
|
||||||
var outbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
var outbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||||
await GenOutbound(item, outbound);
|
await GenOutbound(item, outbound);
|
||||||
outbound.tag = Global.ProxyTag + inbound.port.ToString();
|
outbound.tag = Global.ProxyTag + inbound.port.ToString();
|
||||||
|
@ -354,7 +379,8 @@ public partial class CoreConfigV2rayService(Config config)
|
||||||
var ret = new RetResult();
|
var ret = new RetResult();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (node is not { Port: > 0 })
|
if (node == null
|
||||||
|
|| !node.IsValid())
|
||||||
{
|
{
|
||||||
ret.Msg = ResUI.CheckServerSettings;
|
ret.Msg = ResUI.CheckServerSettings;
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -2,34 +2,87 @@ namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigV2rayService
|
public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
private async Task<int> GenBalancer(V2rayConfig v2rayConfig, EMultipleLoad multipleLoad)
|
private async Task<int> GenObservatory(V2rayConfig v2rayConfig, EMultipleLoad multipleLoad, string baseTagName = Global.ProxyTag)
|
||||||
{
|
{
|
||||||
if (multipleLoad == EMultipleLoad.LeastPing)
|
// Collect all existing subject selectors from both observatories
|
||||||
|
var subjectSelectors = new List<string>();
|
||||||
|
subjectSelectors.AddRange(v2rayConfig.burstObservatory?.subjectSelector ?? []);
|
||||||
|
subjectSelectors.AddRange(v2rayConfig.observatory?.subjectSelector ?? []);
|
||||||
|
|
||||||
|
// Case 1: exact match already exists -> nothing to do
|
||||||
|
if (subjectSelectors.Any(baseTagName.StartsWith))
|
||||||
|
return await Task.FromResult(0);
|
||||||
|
|
||||||
|
// Case 2: prefix match exists -> reuse it and move to the first position
|
||||||
|
var matched = subjectSelectors.FirstOrDefault(s => s.StartsWith(baseTagName));
|
||||||
|
if (matched is not null)
|
||||||
{
|
{
|
||||||
var observatory = new Observatory4Ray
|
baseTagName = matched;
|
||||||
|
|
||||||
|
if (v2rayConfig.burstObservatory?.subjectSelector?.Contains(baseTagName) == true)
|
||||||
{
|
{
|
||||||
subjectSelector = [Global.ProxyTag],
|
v2rayConfig.burstObservatory.subjectSelector.Remove(baseTagName);
|
||||||
probeUrl = AppManager.Instance.Config.SpeedTestItem.SpeedPingTestUrl,
|
v2rayConfig.burstObservatory.subjectSelector.Insert(0, baseTagName);
|
||||||
probeInterval = "3m",
|
}
|
||||||
enableConcurrency = true,
|
|
||||||
};
|
if (v2rayConfig.observatory?.subjectSelector?.Contains(baseTagName) == true)
|
||||||
v2rayConfig.observatory = observatory;
|
{
|
||||||
|
v2rayConfig.observatory.subjectSelector.Remove(baseTagName);
|
||||||
|
v2rayConfig.observatory.subjectSelector.Insert(0, baseTagName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await Task.FromResult(0);
|
||||||
}
|
}
|
||||||
else if (multipleLoad == EMultipleLoad.LeastLoad)
|
|
||||||
|
// Case 3: need to create or insert based on multipleLoad type
|
||||||
|
if (multipleLoad is EMultipleLoad.LeastLoad or EMultipleLoad.Fallback)
|
||||||
{
|
{
|
||||||
var burstObservatory = new BurstObservatory4Ray
|
if (v2rayConfig.burstObservatory is null)
|
||||||
{
|
{
|
||||||
subjectSelector = [Global.ProxyTag],
|
// Create new burst observatory with default ping config
|
||||||
pingConfig = new()
|
v2rayConfig.burstObservatory = new BurstObservatory4Ray
|
||||||
{
|
{
|
||||||
destination = AppManager.Instance.Config.SpeedTestItem.SpeedPingTestUrl,
|
subjectSelector = [baseTagName],
|
||||||
interval = "5m",
|
pingConfig = new()
|
||||||
timeout = "30s",
|
{
|
||||||
sampling = 2,
|
destination = AppManager.Instance.Config.SpeedTestItem.SpeedPingTestUrl,
|
||||||
}
|
interval = "5m",
|
||||||
};
|
timeout = "30s",
|
||||||
v2rayConfig.burstObservatory = burstObservatory;
|
sampling = 2,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
v2rayConfig.burstObservatory.subjectSelector ??= new();
|
||||||
|
v2rayConfig.burstObservatory.subjectSelector.Add(baseTagName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else if (multipleLoad is EMultipleLoad.LeastPing)
|
||||||
|
{
|
||||||
|
if (v2rayConfig.observatory is null)
|
||||||
|
{
|
||||||
|
// Create new observatory with default probe config
|
||||||
|
v2rayConfig.observatory = new Observatory4Ray
|
||||||
|
{
|
||||||
|
subjectSelector = [baseTagName],
|
||||||
|
probeUrl = AppManager.Instance.Config.SpeedTestItem.SpeedPingTestUrl,
|
||||||
|
probeInterval = "3m",
|
||||||
|
enableConcurrency = true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
v2rayConfig.observatory.subjectSelector ??= new();
|
||||||
|
v2rayConfig.observatory.subjectSelector.Add(baseTagName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return await Task.FromResult(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<string> GenBalancer(V2rayConfig v2rayConfig, EMultipleLoad multipleLoad, string selector = Global.ProxyTag)
|
||||||
|
{
|
||||||
var strategyType = multipleLoad switch
|
var strategyType = multipleLoad switch
|
||||||
{
|
{
|
||||||
EMultipleLoad.Random => "random",
|
EMultipleLoad.Random => "random",
|
||||||
|
@ -38,13 +91,22 @@ public partial class CoreConfigV2rayService
|
||||||
EMultipleLoad.LeastLoad => "leastLoad",
|
EMultipleLoad.LeastLoad => "leastLoad",
|
||||||
_ => "roundRobin",
|
_ => "roundRobin",
|
||||||
};
|
};
|
||||||
|
var balancerTag = $"{selector}{Global.BalancerTagSuffix}";
|
||||||
var balancer = new BalancersItem4Ray
|
var balancer = new BalancersItem4Ray
|
||||||
{
|
{
|
||||||
selector = [Global.ProxyTag],
|
selector = [selector],
|
||||||
strategy = new() { type = strategyType },
|
strategy = new()
|
||||||
tag = $"{Global.ProxyTag}-round",
|
{
|
||||||
|
type = strategyType,
|
||||||
|
settings = new()
|
||||||
|
{
|
||||||
|
expected = 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tag = balancerTag,
|
||||||
};
|
};
|
||||||
v2rayConfig.routing.balancers = [balancer];
|
v2rayConfig.routing.balancers ??= new();
|
||||||
return await Task.FromResult(0);
|
v2rayConfig.routing.balancers.Add(balancer);
|
||||||
|
return await Task.FromResult(balancerTag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ namespace ServiceLib.Services.CoreConfig;
|
||||||
|
|
||||||
public partial class CoreConfigV2rayService
|
public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
private async Task<string> ApplyFullConfigTemplate(V2rayConfig v2rayConfig, bool handleBalancerAndRules = false)
|
private async Task<string> ApplyFullConfigTemplate(V2rayConfig v2rayConfig)
|
||||||
{
|
{
|
||||||
var fullConfigTemplate = await AppManager.Instance.GetFullConfigTemplateItem(ECoreType.Xray);
|
var fullConfigTemplate = await AppManager.Instance.GetFullConfigTemplateItem(ECoreType.Xray);
|
||||||
if (fullConfigTemplate == null || !fullConfigTemplate.Enabled || fullConfigTemplate.Config.IsNullOrEmpty())
|
if (fullConfigTemplate == null || !fullConfigTemplate.Enabled || fullConfigTemplate.Config.IsNullOrEmpty())
|
||||||
|
@ -19,7 +19,7 @@ public partial class CoreConfigV2rayService
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle balancer and rules modifications (for multiple load scenarios)
|
// Handle balancer and rules modifications (for multiple load scenarios)
|
||||||
if (handleBalancerAndRules && v2rayConfig.routing?.balancers?.Count > 0)
|
if (v2rayConfig.routing?.balancers?.Count > 0)
|
||||||
{
|
{
|
||||||
var balancer = v2rayConfig.routing.balancers.First();
|
var balancer = v2rayConfig.routing.balancers.First();
|
||||||
|
|
||||||
|
@ -60,6 +60,34 @@ public partial class CoreConfigV2rayService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (v2rayConfig.observatory != null)
|
||||||
|
{
|
||||||
|
if (fullConfigTemplateNode["observatory"] == null)
|
||||||
|
{
|
||||||
|
fullConfigTemplateNode["observatory"] = JsonNode.Parse(JsonUtils.Serialize(v2rayConfig.observatory));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var subjectSelector = v2rayConfig.observatory.subjectSelector;
|
||||||
|
subjectSelector.AddRange(fullConfigTemplateNode["observatory"]?["subjectSelector"]?.AsArray()?.Select(x => x?.GetValue<string>()) ?? []);
|
||||||
|
fullConfigTemplateNode["observatory"]["subjectSelector"] = JsonNode.Parse(JsonUtils.Serialize(subjectSelector.Distinct().ToList()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (v2rayConfig.burstObservatory != null)
|
||||||
|
{
|
||||||
|
if (fullConfigTemplateNode["burstObservatory"] == null)
|
||||||
|
{
|
||||||
|
fullConfigTemplateNode["burstObservatory"] = JsonNode.Parse(JsonUtils.Serialize(v2rayConfig.burstObservatory));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var subjectSelector = v2rayConfig.burstObservatory.subjectSelector;
|
||||||
|
subjectSelector.AddRange(fullConfigTemplateNode["burstObservatory"]?["subjectSelector"]?.AsArray()?.Select(x => x?.GetValue<string>()) ?? []);
|
||||||
|
fullConfigTemplateNode["burstObservatory"]["subjectSelector"] = JsonNode.Parse(JsonUtils.Serialize(subjectSelector.Distinct().ToList()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Handle outbounds - append instead of override
|
// Handle outbounds - append instead of override
|
||||||
var customOutboundsNode = fullConfigTemplateNode["outbounds"] is JsonArray outbounds ? outbounds : new JsonArray();
|
var customOutboundsNode = fullConfigTemplateNode["outbounds"] is JsonArray outbounds ? outbounds : new JsonArray();
|
||||||
foreach (var outbound in v2rayConfig.outbounds)
|
foreach (var outbound in v2rayConfig.outbounds)
|
||||||
|
|
|
@ -142,6 +142,11 @@ public partial class CoreConfigV2rayService
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (item.RuleType == ERuleType.Routing)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var domain in item.Domain)
|
foreach (var domain in item.Domain)
|
||||||
{
|
{
|
||||||
if (domain.StartsWith('#'))
|
if (domain.StartsWith('#'))
|
||||||
|
@ -349,7 +354,7 @@ public partial class CoreConfigV2rayService
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenDnsDomainsCompatible(ProfileItem? node, JsonNode dns, DNSItem? dNSItem)
|
private async Task<int> GenDnsDomainsCompatible(ProfileItem? node, JsonNode dns, DNSItem? dnsItem)
|
||||||
{
|
{
|
||||||
if (node == null)
|
if (node == null)
|
||||||
{
|
{
|
||||||
|
@ -388,7 +393,7 @@ public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
var dnsServer = new DnsServer4Ray()
|
var dnsServer = new DnsServer4Ray()
|
||||||
{
|
{
|
||||||
address = string.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress,
|
address = string.IsNullOrEmpty(dnsItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dnsItem?.DomainDNSAddress,
|
||||||
skipFallback = true,
|
skipFallback = true,
|
||||||
domains = domainList
|
domains = domainList
|
||||||
};
|
};
|
||||||
|
|
|
@ -480,6 +480,60 @@ public partial class CoreConfigV2rayService
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<int> GenGroupOutbound(ProfileItem node, V2rayConfig v2rayConfig, string baseTagName = Global.ProxyTag, bool ignoreOriginChain = false)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!node.ConfigType.IsGroupType())
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
var hasCycle = ProfileGroupItemManager.HasCycle(node.IndexId);
|
||||||
|
if (hasCycle)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var (childProfiles, profileGroupItem) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId);
|
||||||
|
if (childProfiles.Count <= 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
switch (node.ConfigType)
|
||||||
|
{
|
||||||
|
case EConfigType.PolicyGroup:
|
||||||
|
if (ignoreOriginChain)
|
||||||
|
{
|
||||||
|
await GenOutboundsList(childProfiles, v2rayConfig, baseTagName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await GenOutboundsListWithChain(childProfiles, v2rayConfig, baseTagName);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EConfigType.ProxyChain:
|
||||||
|
await GenChainOutboundsList(childProfiles, v2rayConfig, baseTagName);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//add balancers
|
||||||
|
if (node.ConfigType == EConfigType.PolicyGroup)
|
||||||
|
{
|
||||||
|
await GenObservatory(v2rayConfig, profileGroupItem.MultipleLoad, baseTagName);
|
||||||
|
await GenBalancer(v2rayConfig, profileGroupItem.MultipleLoad, baseTagName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logging.SaveLog(_tag, ex);
|
||||||
|
}
|
||||||
|
return await Task.FromResult(0);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<int> GenMoreOutbounds(ProfileItem node, V2rayConfig v2rayConfig)
|
private async Task<int> GenMoreOutbounds(ProfileItem node, V2rayConfig v2rayConfig)
|
||||||
{
|
{
|
||||||
//fragment proxy
|
//fragment proxy
|
||||||
|
@ -552,7 +606,7 @@ public partial class CoreConfigV2rayService
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenOutboundsList(List<ProfileItem> nodes, V2rayConfig v2rayConfig)
|
private async Task<int> GenOutboundsListWithChain(List<ProfileItem> nodes, V2rayConfig v2rayConfig, string baseTagName = Global.ProxyTag)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -577,6 +631,25 @@ public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
index++;
|
index++;
|
||||||
|
|
||||||
|
if (node.ConfigType.IsGroupType())
|
||||||
|
{
|
||||||
|
var (childProfiles, _) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId);
|
||||||
|
if (childProfiles.Count <= 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var childBaseTagName = $"{baseTagName}-{index}";
|
||||||
|
var ret = node.ConfigType switch
|
||||||
|
{
|
||||||
|
EConfigType.PolicyGroup =>
|
||||||
|
await GenOutboundsListWithChain(childProfiles, v2rayConfig, childBaseTagName),
|
||||||
|
EConfigType.ProxyChain =>
|
||||||
|
await GenChainOutboundsList(childProfiles, v2rayConfig, childBaseTagName),
|
||||||
|
_ => throw new NotImplementedException()
|
||||||
|
};
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Handle proxy chain
|
// Handle proxy chain
|
||||||
string? prevTag = null;
|
string? prevTag = null;
|
||||||
var currentOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
var currentOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||||
|
@ -590,7 +663,7 @@ public partial class CoreConfigV2rayService
|
||||||
|
|
||||||
// current proxy
|
// current proxy
|
||||||
await GenOutbound(node, currentOutbound);
|
await GenOutbound(node, currentOutbound);
|
||||||
currentOutbound.tag = $"{Global.ProxyTag}-{index}";
|
currentOutbound.tag = $"{baseTagName}-{index}";
|
||||||
|
|
||||||
if (!node.Subid.IsNullOrEmpty())
|
if (!node.Subid.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
|
@ -606,7 +679,7 @@ public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
var prevOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
var prevOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||||
await GenOutbound(prevNode, prevOutbound);
|
await GenOutbound(prevNode, prevOutbound);
|
||||||
prevTag = $"prev-{Global.ProxyTag}-{++prevIndex}";
|
prevTag = $"prev-{baseTagName}-{++prevIndex}";
|
||||||
prevOutbound.tag = prevTag;
|
prevOutbound.tag = prevTag;
|
||||||
prevOutbounds.Add(prevOutbound);
|
prevOutbounds.Add(prevOutbound);
|
||||||
}
|
}
|
||||||
|
@ -628,9 +701,17 @@ public partial class CoreConfigV2rayService
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge results: first the main chain outbounds, then other outbounds, and finally utility outbounds
|
// Merge results: first the main chain outbounds, then other outbounds, and finally utility outbounds
|
||||||
resultOutbounds.AddRange(prevOutbounds);
|
if (baseTagName == Global.ProxyTag)
|
||||||
resultOutbounds.AddRange(v2rayConfig.outbounds);
|
{
|
||||||
v2rayConfig.outbounds = resultOutbounds;
|
resultOutbounds.AddRange(prevOutbounds);
|
||||||
|
resultOutbounds.AddRange(v2rayConfig.outbounds);
|
||||||
|
v2rayConfig.outbounds = resultOutbounds;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
v2rayConfig.outbounds.AddRange(prevOutbounds);
|
||||||
|
v2rayConfig.outbounds.AddRange(resultOutbounds);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -692,4 +773,110 @@ public partial class CoreConfigV2rayService
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<int> GenOutboundsList(List<ProfileItem> nodes, V2rayConfig v2rayConfig, string baseTagName = Global.ProxyTag)
|
||||||
|
{
|
||||||
|
var resultOutbounds = new List<Outbounds4Ray>();
|
||||||
|
for (var i = 0; i < nodes.Count; i++)
|
||||||
|
{
|
||||||
|
var node = nodes[i];
|
||||||
|
if (node == null)
|
||||||
|
continue;
|
||||||
|
if (node.ConfigType.IsGroupType())
|
||||||
|
{
|
||||||
|
var (childProfiles, _) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId);
|
||||||
|
if (childProfiles.Count <= 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var childBaseTagName = $"{baseTagName}-{i + 1}";
|
||||||
|
var ret = node.ConfigType switch
|
||||||
|
{
|
||||||
|
EConfigType.PolicyGroup =>
|
||||||
|
await GenOutboundsListWithChain(childProfiles, v2rayConfig, childBaseTagName),
|
||||||
|
EConfigType.ProxyChain =>
|
||||||
|
await GenChainOutboundsList(childProfiles, v2rayConfig, childBaseTagName),
|
||||||
|
_ => throw new NotImplementedException()
|
||||||
|
};
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
|
||||||
|
if (txtOutbound.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
var outbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||||
|
var result = await GenOutbound(node, outbound);
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
outbound.tag = baseTagName + (i + 1).ToString();
|
||||||
|
resultOutbounds.Add(outbound);
|
||||||
|
}
|
||||||
|
if (baseTagName == Global.ProxyTag)
|
||||||
|
{
|
||||||
|
resultOutbounds.AddRange(v2rayConfig.outbounds);
|
||||||
|
v2rayConfig.outbounds = resultOutbounds;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
v2rayConfig.outbounds.AddRange(resultOutbounds);
|
||||||
|
}
|
||||||
|
return await Task.FromResult(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<int> GenChainOutboundsList(List<ProfileItem> nodes, V2rayConfig v2rayConfig, string baseTagName = Global.ProxyTag)
|
||||||
|
{
|
||||||
|
// Based on actual network flow instead of data packets
|
||||||
|
var nodesReverse = nodes.AsEnumerable().Reverse().ToList();
|
||||||
|
var resultOutbounds = new List<Outbounds4Ray>();
|
||||||
|
for (var i = 0; i < nodesReverse.Count; i++)
|
||||||
|
{
|
||||||
|
var node = nodesReverse[i];
|
||||||
|
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
|
||||||
|
if (txtOutbound.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
var outbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||||
|
var result = await GenOutbound(node, outbound);
|
||||||
|
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == 0)
|
||||||
|
{
|
||||||
|
outbound.tag = baseTagName;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// avoid v2ray observe
|
||||||
|
outbound.tag = "chain-" + baseTagName + i.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i != nodesReverse.Count - 1)
|
||||||
|
{
|
||||||
|
outbound.streamSettings.sockopt = new()
|
||||||
|
{
|
||||||
|
dialerProxy = "chain-" + baseTagName + (i + 1).ToString()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
resultOutbounds.Add(outbound);
|
||||||
|
}
|
||||||
|
if (baseTagName == Global.ProxyTag)
|
||||||
|
{
|
||||||
|
resultOutbounds.AddRange(v2rayConfig.outbounds);
|
||||||
|
v2rayConfig.outbounds = resultOutbounds;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
v2rayConfig.outbounds.AddRange(resultOutbounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await Task.FromResult(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,11 +20,18 @@ public partial class CoreConfigV2rayService
|
||||||
var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet);
|
var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet);
|
||||||
foreach (var item in rules)
|
foreach (var item in rules)
|
||||||
{
|
{
|
||||||
if (item.Enabled)
|
if (!item.Enabled)
|
||||||
{
|
{
|
||||||
var item2 = JsonUtils.Deserialize<RulesItem4Ray>(JsonUtils.Serialize(item));
|
continue;
|
||||||
await GenRoutingUserRule(item2, v2rayConfig);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (item.RuleType == ERuleType.DNS)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var item2 = JsonUtils.Deserialize<RulesItem4Ray>(JsonUtils.Serialize(item));
|
||||||
|
await GenRoutingUserRule(item2, v2rayConfig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -125,18 +132,30 @@ public partial class CoreConfigV2rayService
|
||||||
}
|
}
|
||||||
|
|
||||||
var node = await AppManager.Instance.GetProfileItemViaRemarks(outboundTag);
|
var node = await AppManager.Instance.GetProfileItemViaRemarks(outboundTag);
|
||||||
|
|
||||||
if (node == null
|
if (node == null
|
||||||
|| !Global.XraySupportConfigType.Contains(node.ConfigType))
|
|| (!Global.XraySupportConfigType.Contains(node.ConfigType)
|
||||||
|
&& !node.ConfigType.IsGroupType()))
|
||||||
{
|
{
|
||||||
return Global.ProxyTag;
|
return Global.ProxyTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
var tag = Global.ProxyTag + node.IndexId.ToString();
|
var tag = $"{node.IndexId}-{Global.ProxyTag}";
|
||||||
if (v2rayConfig.outbounds.Any(p => p.tag == tag))
|
if (v2rayConfig.outbounds.Any(p => p.tag == tag))
|
||||||
{
|
{
|
||||||
return tag;
|
return tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (node.ConfigType.IsGroupType())
|
||||||
|
{
|
||||||
|
var ret = await GenGroupOutbound(node, v2rayConfig, tag);
|
||||||
|
if (ret == 0)
|
||||||
|
{
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
return Global.ProxyTag;
|
||||||
|
}
|
||||||
|
|
||||||
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
|
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
|
||||||
var outbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
var outbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||||
await GenOutbound(node, outbound);
|
await GenOutbound(node, outbound);
|
||||||
|
|
183
v2rayN/ServiceLib/Services/ProcessService.cs
Normal file
183
v2rayN/ServiceLib/Services/ProcessService.cs
Normal file
|
@ -0,0 +1,183 @@
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace ServiceLib.Services;
|
||||||
|
|
||||||
|
public class ProcessService : IDisposable
|
||||||
|
{
|
||||||
|
private readonly Process _process;
|
||||||
|
private readonly Func<bool, string, Task>? _updateFunc;
|
||||||
|
private bool _isDisposed;
|
||||||
|
|
||||||
|
public int Id => _process.Id;
|
||||||
|
public IntPtr Handle => _process.Handle;
|
||||||
|
public bool HasExited => _process.HasExited;
|
||||||
|
|
||||||
|
public ProcessService(
|
||||||
|
string fileName,
|
||||||
|
string arguments,
|
||||||
|
string workingDirectory,
|
||||||
|
bool displayLog,
|
||||||
|
bool redirectInput,
|
||||||
|
Dictionary<string, string>? environmentVars,
|
||||||
|
Func<bool, string, Task>? updateFunc)
|
||||||
|
{
|
||||||
|
_updateFunc = updateFunc;
|
||||||
|
|
||||||
|
_process = new Process
|
||||||
|
{
|
||||||
|
StartInfo = new ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = fileName,
|
||||||
|
Arguments = arguments,
|
||||||
|
WorkingDirectory = workingDirectory,
|
||||||
|
UseShellExecute = false,
|
||||||
|
RedirectStandardInput = redirectInput,
|
||||||
|
RedirectStandardOutput = displayLog,
|
||||||
|
RedirectStandardError = displayLog,
|
||||||
|
CreateNoWindow = true,
|
||||||
|
StandardOutputEncoding = displayLog ? Encoding.UTF8 : null,
|
||||||
|
StandardErrorEncoding = displayLog ? Encoding.UTF8 : null,
|
||||||
|
},
|
||||||
|
EnableRaisingEvents = true
|
||||||
|
};
|
||||||
|
|
||||||
|
if (environmentVars != null)
|
||||||
|
{
|
||||||
|
foreach (var kv in environmentVars)
|
||||||
|
{
|
||||||
|
_process.StartInfo.Environment[kv.Key] = kv.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (displayLog)
|
||||||
|
{
|
||||||
|
RegisterEventHandlers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task StartAsync(string pwd = null)
|
||||||
|
{
|
||||||
|
_process.Start();
|
||||||
|
|
||||||
|
if (_process.StartInfo.RedirectStandardOutput)
|
||||||
|
{
|
||||||
|
_process.BeginOutputReadLine();
|
||||||
|
_process.BeginErrorReadLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_process.StartInfo.RedirectStandardInput)
|
||||||
|
{
|
||||||
|
await Task.Delay(10);
|
||||||
|
await _process.StandardInput.WriteLineAsync(pwd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task StopAsync()
|
||||||
|
{
|
||||||
|
if (_process.HasExited)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_process.StartInfo.RedirectStandardOutput)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_process.CancelOutputRead();
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_process.CancelErrorRead();
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Utils.IsNonWindows())
|
||||||
|
{
|
||||||
|
_process.Kill(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_process.Kill();
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
|
||||||
|
await Task.Delay(100);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await _updateFunc?.Invoke(true, ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RegisterEventHandlers()
|
||||||
|
{
|
||||||
|
void dataHandler(object sender, DataReceivedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Data.IsNotEmpty())
|
||||||
|
{
|
||||||
|
_ = _updateFunc?.Invoke(false, e.Data + Environment.NewLine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_process.OutputDataReceived += dataHandler;
|
||||||
|
_process.ErrorDataReceived += dataHandler;
|
||||||
|
|
||||||
|
_process.Exited += (s, e) =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_process.OutputDataReceived -= dataHandler;
|
||||||
|
_process.ErrorDataReceived -= dataHandler;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_isDisposed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!_process.HasExited)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_process.CancelOutputRead();
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_process.CancelErrorRead();
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
|
||||||
|
_process.Kill();
|
||||||
|
}
|
||||||
|
|
||||||
|
_process.Dispose();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_updateFunc?.Invoke(true, ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
_isDisposed = true;
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
}
|
|
@ -64,7 +64,7 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
|
||||||
var lstSelected = new List<ServerTestItem>();
|
var lstSelected = new List<ServerTestItem>();
|
||||||
foreach (var it in selecteds)
|
foreach (var it in selecteds)
|
||||||
{
|
{
|
||||||
if (it.ConfigType == EConfigType.Custom)
|
if (it.ConfigType.IsComplexType())
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -116,10 +116,6 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
|
||||||
List<Task> tasks = [];
|
List<Task> tasks = [];
|
||||||
foreach (var it in selecteds)
|
foreach (var it in selecteds)
|
||||||
{
|
{
|
||||||
if (it.ConfigType == EConfigType.Custom)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
tasks.Add(Task.Run(async () =>
|
tasks.Add(Task.Run(async () =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -182,11 +178,11 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
|
||||||
|
|
||||||
private async Task<bool> RunRealPingAsync(List<ServerTestItem> selecteds, string exitLoopKey)
|
private async Task<bool> RunRealPingAsync(List<ServerTestItem> selecteds, string exitLoopKey)
|
||||||
{
|
{
|
||||||
var pid = -1;
|
ProcessService processService = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
pid = await CoreManager.Instance.LoadCoreConfigSpeedtest(selecteds);
|
processService = await CoreManager.Instance.LoadCoreConfigSpeedtest(selecteds);
|
||||||
if (pid < 0)
|
if (processService is null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -199,10 +195,6 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (it.ConfigType == EConfigType.Custom)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
tasks.Add(Task.Run(async () =>
|
tasks.Add(Task.Run(async () =>
|
||||||
{
|
{
|
||||||
await DoRealPing(it);
|
await DoRealPing(it);
|
||||||
|
@ -216,9 +208,9 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
if (pid > 0)
|
if (processService != null)
|
||||||
{
|
{
|
||||||
await ProcUtils.ProcessKill(pid);
|
await processService?.StopAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -236,19 +228,15 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
|
||||||
await UpdateFunc(it.IndexId, "", ResUI.SpeedtestingSkip);
|
await UpdateFunc(it.IndexId, "", ResUI.SpeedtestingSkip);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (it.ConfigType == EConfigType.Custom)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
await concurrencySemaphore.WaitAsync();
|
await concurrencySemaphore.WaitAsync();
|
||||||
|
|
||||||
tasks.Add(Task.Run(async () =>
|
tasks.Add(Task.Run(async () =>
|
||||||
{
|
{
|
||||||
var pid = -1;
|
ProcessService processService = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
pid = await CoreManager.Instance.LoadCoreConfigSpeedtest(it);
|
processService = await CoreManager.Instance.LoadCoreConfigSpeedtest(it);
|
||||||
if (pid < 0)
|
if (processService is null)
|
||||||
{
|
{
|
||||||
await UpdateFunc(it.IndexId, "", ResUI.FailedToRunCore);
|
await UpdateFunc(it.IndexId, "", ResUI.FailedToRunCore);
|
||||||
}
|
}
|
||||||
|
@ -275,9 +263,9 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
if (pid > 0)
|
if (processService != null)
|
||||||
{
|
{
|
||||||
await ProcUtils.ProcessKill(pid);
|
await processService?.StopAsync();
|
||||||
}
|
}
|
||||||
concurrencySemaphore.Release();
|
concurrencySemaphore.Release();
|
||||||
}
|
}
|
||||||
|
@ -351,7 +339,7 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
|
||||||
{
|
{
|
||||||
List<List<ServerTestItem>> lstTest = new();
|
List<List<ServerTestItem>> lstTest = new();
|
||||||
var lst1 = lstSelected.Where(t => Global.XraySupportConfigType.Contains(t.ConfigType)).ToList();
|
var lst1 = lstSelected.Where(t => Global.XraySupportConfigType.Contains(t.ConfigType)).ToList();
|
||||||
var lst2 = lstSelected.Where(t => Global.SingboxSupportConfigType.Contains(t.ConfigType) && !Global.XraySupportConfigType.Contains(t.ConfigType)).ToList();
|
var lst2 = lstSelected.Where(t => Global.SingboxOnlyConfigType.Contains(t.ConfigType)).ToList();
|
||||||
|
|
||||||
for (var num = 0; num < (int)Math.Ceiling(lst1.Count * 1.0 / pageSize); num++)
|
for (var num = 0; num < (int)Math.Ceiling(lst1.Count * 1.0 / pageSize); num++)
|
||||||
{
|
{
|
||||||
|
|
238
v2rayN/ServiceLib/ViewModels/AddGroupServerViewModel.cs
Normal file
238
v2rayN/ServiceLib/ViewModels/AddGroupServerViewModel.cs
Normal file
|
@ -0,0 +1,238 @@
|
||||||
|
using System.Reactive;
|
||||||
|
using DynamicData.Binding;
|
||||||
|
using ReactiveUI;
|
||||||
|
using ReactiveUI.Fody.Helpers;
|
||||||
|
|
||||||
|
namespace ServiceLib.ViewModels;
|
||||||
|
|
||||||
|
public class AddGroupServerViewModel : MyReactiveObject
|
||||||
|
{
|
||||||
|
[Reactive]
|
||||||
|
public ProfileItem SelectedSource { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public ProfileItem SelectedChild { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public IList<ProfileItem> SelectedChildren { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public string? CoreType { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public string? PolicyGroupType { get; set; }
|
||||||
|
|
||||||
|
public IObservableCollection<ProfileItem> ChildItemsObs { get; } = new ObservableCollectionExtended<ProfileItem>();
|
||||||
|
|
||||||
|
//public ReactiveCommand<Unit, Unit> AddCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> RemoveCmd { get; }
|
||||||
|
|
||||||
|
public ReactiveCommand<Unit, Unit> MoveTopCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> MoveUpCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> MoveDownCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> MoveBottomCmd { get; }
|
||||||
|
|
||||||
|
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
||||||
|
|
||||||
|
public AddGroupServerViewModel(ProfileItem profileItem, Func<EViewAction, object?, Task<bool>>? updateView)
|
||||||
|
{
|
||||||
|
_config = AppManager.Instance.Config;
|
||||||
|
_updateView = updateView;
|
||||||
|
|
||||||
|
var canEditRemove = this.WhenAnyValue(
|
||||||
|
x => x.SelectedChild,
|
||||||
|
SelectedChild => SelectedChild != null && !SelectedChild.Remarks.IsNullOrEmpty());
|
||||||
|
|
||||||
|
RemoveCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
|
{
|
||||||
|
await ChildRemoveAsync();
|
||||||
|
}, canEditRemove);
|
||||||
|
MoveTopCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
|
{
|
||||||
|
await MoveServer(EMove.Top);
|
||||||
|
}, canEditRemove);
|
||||||
|
MoveUpCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
|
{
|
||||||
|
await MoveServer(EMove.Up);
|
||||||
|
}, canEditRemove);
|
||||||
|
MoveDownCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
|
{
|
||||||
|
await MoveServer(EMove.Down);
|
||||||
|
}, canEditRemove);
|
||||||
|
MoveBottomCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
|
{
|
||||||
|
await MoveServer(EMove.Bottom);
|
||||||
|
}, canEditRemove);
|
||||||
|
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
|
{
|
||||||
|
await SaveServerAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
SelectedSource = profileItem.IndexId.IsNullOrEmpty() ? profileItem : JsonUtils.DeepCopy(profileItem);
|
||||||
|
|
||||||
|
CoreType = (SelectedSource?.CoreType ?? ECoreType.Xray).ToString();
|
||||||
|
|
||||||
|
ProfileGroupItemManager.Instance.TryGet(profileItem.IndexId, out var profileGroup);
|
||||||
|
PolicyGroupType = (profileGroup?.MultipleLoad ?? EMultipleLoad.LeastPing) switch
|
||||||
|
{
|
||||||
|
EMultipleLoad.LeastPing => ResUI.TbLeastPing,
|
||||||
|
EMultipleLoad.Fallback => ResUI.TbFallback,
|
||||||
|
EMultipleLoad.Random => ResUI.TbRandom,
|
||||||
|
EMultipleLoad.RoundRobin => ResUI.TbRoundRobin,
|
||||||
|
EMultipleLoad.LeastLoad => ResUI.TbLeastLoad,
|
||||||
|
_ => ResUI.TbLeastPing,
|
||||||
|
};
|
||||||
|
|
||||||
|
_ = Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Init()
|
||||||
|
{
|
||||||
|
var childItemMulti = ProfileGroupItemManager.Instance.GetOrCreateAndMarkDirty(SelectedSource?.IndexId);
|
||||||
|
if (childItemMulti != null)
|
||||||
|
{
|
||||||
|
var childIndexIds = childItemMulti.ChildItems.IsNullOrEmpty() ? new List<string>() : Utils.String2List(childItemMulti.ChildItems);
|
||||||
|
foreach (var item in childIndexIds)
|
||||||
|
{
|
||||||
|
var child = await AppManager.Instance.GetProfileItem(item);
|
||||||
|
if (child == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ChildItemsObs.Add(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ChildRemoveAsync()
|
||||||
|
{
|
||||||
|
if (SelectedChild == null || SelectedChild.IndexId.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
NoticeManager.Instance.Enqueue(ResUI.PleaseSelectServer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
foreach (var it in SelectedChildren ?? [SelectedChild])
|
||||||
|
{
|
||||||
|
if (it != null)
|
||||||
|
{
|
||||||
|
ChildItemsObs.Remove(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task MoveServer(EMove eMove)
|
||||||
|
{
|
||||||
|
if (SelectedChild == null || SelectedChild.IndexId.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
NoticeManager.Instance.Enqueue(ResUI.PleaseSelectServer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var index = ChildItemsObs.IndexOf(SelectedChild);
|
||||||
|
if (index < 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var selectedChild = JsonUtils.DeepCopy(SelectedChild);
|
||||||
|
switch (eMove)
|
||||||
|
{
|
||||||
|
case EMove.Top:
|
||||||
|
if (index == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ChildItemsObs.RemoveAt(index);
|
||||||
|
ChildItemsObs.Insert(0, selectedChild);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EMove.Up:
|
||||||
|
if (index == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ChildItemsObs.RemoveAt(index);
|
||||||
|
ChildItemsObs.Insert(index - 1, selectedChild);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EMove.Down:
|
||||||
|
if (index == ChildItemsObs.Count - 1)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ChildItemsObs.RemoveAt(index);
|
||||||
|
ChildItemsObs.Insert(index + 1, selectedChild);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EMove.Bottom:
|
||||||
|
if (index == ChildItemsObs.Count - 1)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ChildItemsObs.RemoveAt(index);
|
||||||
|
ChildItemsObs.Add(selectedChild);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
await Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SaveServerAsync()
|
||||||
|
{
|
||||||
|
var remarks = SelectedSource.Remarks;
|
||||||
|
if (remarks.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
NoticeManager.Instance.Enqueue(ResUI.PleaseFillRemarks);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ChildItemsObs.Count == 0)
|
||||||
|
{
|
||||||
|
NoticeManager.Instance.Enqueue(ResUI.PleaseAddAtLeastOneServer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SelectedSource.CoreType = CoreType.IsNullOrEmpty() ? ECoreType.Xray : (ECoreType)Enum.Parse(typeof(ECoreType), CoreType);
|
||||||
|
if (SelectedSource.CoreType is not (ECoreType.Xray or ECoreType.sing_box) ||
|
||||||
|
SelectedSource.ConfigType is not (EConfigType.ProxyChain or EConfigType.PolicyGroup))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var childIndexIds = new List<string>();
|
||||||
|
foreach (var item in ChildItemsObs)
|
||||||
|
{
|
||||||
|
if (item.IndexId.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
childIndexIds.Add(item.IndexId);
|
||||||
|
}
|
||||||
|
var profileGroup = ProfileGroupItemManager.Instance.GetOrCreateAndMarkDirty(SelectedSource.IndexId);
|
||||||
|
profileGroup.ChildItems = Utils.List2String(childIndexIds);
|
||||||
|
profileGroup.MultipleLoad = PolicyGroupType switch
|
||||||
|
{
|
||||||
|
var s when s == ResUI.TbLeastPing => EMultipleLoad.LeastPing,
|
||||||
|
var s when s == ResUI.TbFallback => EMultipleLoad.Fallback,
|
||||||
|
var s when s == ResUI.TbRandom => EMultipleLoad.Random,
|
||||||
|
var s when s == ResUI.TbRoundRobin => EMultipleLoad.RoundRobin,
|
||||||
|
var s when s == ResUI.TbLeastLoad => EMultipleLoad.LeastLoad,
|
||||||
|
_ => EMultipleLoad.LeastPing,
|
||||||
|
};
|
||||||
|
|
||||||
|
var hasCycle = ProfileGroupItemManager.HasCycle(profileGroup.IndexId);
|
||||||
|
if (hasCycle)
|
||||||
|
{
|
||||||
|
NoticeManager.Instance.Enqueue(string.Format(ResUI.GroupSelfReference, remarks));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await ConfigHandler.AddGroupServerCommon(_config, SelectedSource, profileGroup, true) == 0)
|
||||||
|
{
|
||||||
|
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
|
||||||
|
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NoticeManager.Instance.Enqueue(ResUI.OperationFailed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Reactive;
|
using System.Reactive;
|
||||||
|
using System.Reactive.Linq;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
using ReactiveUI.Fody.Helpers;
|
using ReactiveUI.Fody.Helpers;
|
||||||
|
|
||||||
|
@ -12,8 +13,6 @@ public class DNSSettingViewModel : MyReactiveObject
|
||||||
[Reactive] public bool? BlockBindingQuery { get; set; }
|
[Reactive] public bool? BlockBindingQuery { get; set; }
|
||||||
[Reactive] public string? DirectDNS { get; set; }
|
[Reactive] public string? DirectDNS { get; set; }
|
||||||
[Reactive] public string? RemoteDNS { 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? RayStrategy4Freedom { get; set; }
|
||||||
[Reactive] public string? SingboxStrategy4Direct { get; set; }
|
[Reactive] public string? SingboxStrategy4Direct { get; set; }
|
||||||
[Reactive] public string? SingboxStrategy4Proxy { get; set; }
|
[Reactive] public string? SingboxStrategy4Proxy { get; set; }
|
||||||
|
@ -32,6 +31,8 @@ public class DNSSettingViewModel : MyReactiveObject
|
||||||
[Reactive] public bool RayCustomDNSEnableCompatible { get; set; }
|
[Reactive] public bool RayCustomDNSEnableCompatible { get; set; }
|
||||||
[Reactive] public bool SBCustomDNSEnableCompatible { get; set; }
|
[Reactive] public bool SBCustomDNSEnableCompatible { get; set; }
|
||||||
|
|
||||||
|
[ObservableAsProperty] public bool IsSimpleDNSEnabled { get; }
|
||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> ImportDefConfig4V2rayCompatibleCmd { get; }
|
public ReactiveCommand<Unit, Unit> ImportDefConfig4V2rayCompatibleCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> ImportDefConfig4SingboxCompatibleCmd { get; }
|
public ReactiveCommand<Unit, Unit> ImportDefConfig4SingboxCompatibleCmd { get; }
|
||||||
|
@ -55,6 +56,10 @@ public class DNSSettingViewModel : MyReactiveObject
|
||||||
await Task.CompletedTask;
|
await Task.CompletedTask;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.WhenAnyValue(x => x.RayCustomDNSEnableCompatible, x => x.SBCustomDNSEnableCompatible)
|
||||||
|
.Select(x => !(x.Item1 && x.Item2))
|
||||||
|
.ToPropertyEx(this, x => x.IsSimpleDNSEnabled);
|
||||||
|
|
||||||
_ = Init();
|
_ = Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,8 +74,6 @@ public class DNSSettingViewModel : MyReactiveObject
|
||||||
DirectDNS = item.DirectDNS;
|
DirectDNS = item.DirectDNS;
|
||||||
RemoteDNS = item.RemoteDNS;
|
RemoteDNS = item.RemoteDNS;
|
||||||
RayStrategy4Freedom = item.RayStrategy4Freedom;
|
RayStrategy4Freedom = item.RayStrategy4Freedom;
|
||||||
SingboxOutboundsResolveDNS = item.SingboxOutboundsResolveDNS;
|
|
||||||
SingboxFinalResolveDNS = item.SingboxFinalResolveDNS;
|
|
||||||
SingboxStrategy4Direct = item.SingboxStrategy4Direct;
|
SingboxStrategy4Direct = item.SingboxStrategy4Direct;
|
||||||
SingboxStrategy4Proxy = item.SingboxStrategy4Proxy;
|
SingboxStrategy4Proxy = item.SingboxStrategy4Proxy;
|
||||||
Hosts = item.Hosts;
|
Hosts = item.Hosts;
|
||||||
|
@ -100,8 +103,6 @@ public class DNSSettingViewModel : MyReactiveObject
|
||||||
_config.SimpleDNSItem.DirectDNS = DirectDNS;
|
_config.SimpleDNSItem.DirectDNS = DirectDNS;
|
||||||
_config.SimpleDNSItem.RemoteDNS = RemoteDNS;
|
_config.SimpleDNSItem.RemoteDNS = RemoteDNS;
|
||||||
_config.SimpleDNSItem.RayStrategy4Freedom = RayStrategy4Freedom;
|
_config.SimpleDNSItem.RayStrategy4Freedom = RayStrategy4Freedom;
|
||||||
_config.SimpleDNSItem.SingboxOutboundsResolveDNS = SingboxOutboundsResolveDNS;
|
|
||||||
_config.SimpleDNSItem.SingboxFinalResolveDNS = SingboxFinalResolveDNS;
|
|
||||||
_config.SimpleDNSItem.SingboxStrategy4Direct = SingboxStrategy4Direct;
|
_config.SimpleDNSItem.SingboxStrategy4Direct = SingboxStrategy4Direct;
|
||||||
_config.SimpleDNSItem.SingboxStrategy4Proxy = SingboxStrategy4Proxy;
|
_config.SimpleDNSItem.SingboxStrategy4Proxy = SingboxStrategy4Proxy;
|
||||||
_config.SimpleDNSItem.Hosts = Hosts;
|
_config.SimpleDNSItem.Hosts = Hosts;
|
||||||
|
|
|
@ -23,6 +23,8 @@ public class MainWindowViewModel : MyReactiveObject
|
||||||
public ReactiveCommand<Unit, Unit> AddWireguardServerCmd { get; }
|
public ReactiveCommand<Unit, Unit> AddWireguardServerCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> AddAnytlsServerCmd { get; }
|
public ReactiveCommand<Unit, Unit> AddAnytlsServerCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> AddCustomServerCmd { get; }
|
public ReactiveCommand<Unit, Unit> AddCustomServerCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> AddPolicyGroupServerCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> AddProxyChainServerCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> AddServerViaClipboardCmd { get; }
|
public ReactiveCommand<Unit, Unit> AddServerViaClipboardCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> AddServerViaScanCmd { get; }
|
public ReactiveCommand<Unit, Unit> AddServerViaScanCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> AddServerViaImageCmd { get; }
|
public ReactiveCommand<Unit, Unit> AddServerViaImageCmd { get; }
|
||||||
|
@ -122,6 +124,14 @@ public class MainWindowViewModel : MyReactiveObject
|
||||||
{
|
{
|
||||||
await AddServerAsync(true, EConfigType.Custom);
|
await AddServerAsync(true, EConfigType.Custom);
|
||||||
});
|
});
|
||||||
|
AddPolicyGroupServerCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
|
{
|
||||||
|
await AddServerAsync(true, EConfigType.PolicyGroup);
|
||||||
|
});
|
||||||
|
AddProxyChainServerCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
|
{
|
||||||
|
await AddServerAsync(true, EConfigType.ProxyChain);
|
||||||
|
});
|
||||||
AddServerViaClipboardCmd = ReactiveCommand.CreateFromTask(async () =>
|
AddServerViaClipboardCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
{
|
{
|
||||||
await AddServerViaClipboardAsync(null);
|
await AddServerViaClipboardAsync(null);
|
||||||
|
@ -252,6 +262,7 @@ public class MainWindowViewModel : MyReactiveObject
|
||||||
await ConfigHandler.InitBuiltinDNS(_config);
|
await ConfigHandler.InitBuiltinDNS(_config);
|
||||||
await ConfigHandler.InitBuiltinFullConfigTemplate(_config);
|
await ConfigHandler.InitBuiltinFullConfigTemplate(_config);
|
||||||
await ProfileExManager.Instance.Init();
|
await ProfileExManager.Instance.Init();
|
||||||
|
await ProfileGroupItemManager.Instance.Init();
|
||||||
await CoreManager.Instance.Init(_config, UpdateHandler);
|
await CoreManager.Instance.Init(_config, UpdateHandler);
|
||||||
TaskManager.Instance.RegUpdateTask(_config, UpdateTaskHandler);
|
TaskManager.Instance.RegUpdateTask(_config, UpdateTaskHandler);
|
||||||
|
|
||||||
|
@ -263,7 +274,6 @@ public class MainWindowViewModel : MyReactiveObject
|
||||||
|
|
||||||
BlReloadEnabled = true;
|
BlReloadEnabled = true;
|
||||||
await Reload();
|
await Reload();
|
||||||
await AutoHideStartup();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Init
|
#endregion Init
|
||||||
|
@ -340,6 +350,10 @@ public class MainWindowViewModel : MyReactiveObject
|
||||||
{
|
{
|
||||||
ret = await _updateView?.Invoke(EViewAction.AddServer2Window, item);
|
ret = await _updateView?.Invoke(EViewAction.AddServer2Window, item);
|
||||||
}
|
}
|
||||||
|
else if (eConfigType.IsGroupType())
|
||||||
|
{
|
||||||
|
ret = await _updateView?.Invoke(EViewAction.AddGroupServerWindow, item);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ret = await _updateView?.Invoke(EViewAction.AddServerWindow, item);
|
ret = await _updateView?.Invoke(EViewAction.AddServerWindow, item);
|
||||||
|
@ -524,6 +538,18 @@ public class MainWindowViewModel : MyReactiveObject
|
||||||
|
|
||||||
BlReloadEnabled = false;
|
BlReloadEnabled = false;
|
||||||
|
|
||||||
|
var msgs = await ActionPrecheckManager.Instance.Check(_config.IndexId);
|
||||||
|
if (msgs.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (var msg in msgs)
|
||||||
|
{
|
||||||
|
NoticeManager.Instance.SendMessage(msg);
|
||||||
|
}
|
||||||
|
NoticeManager.Instance.Enqueue(Utils.List2String(msgs.Take(10).ToList(), true));
|
||||||
|
BlReloadEnabled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await Task.Run(async () =>
|
await Task.Run(async () =>
|
||||||
{
|
{
|
||||||
await LoadCore();
|
await LoadCore();
|
||||||
|
@ -561,15 +587,6 @@ public class MainWindowViewModel : MyReactiveObject
|
||||||
await CoreManager.Instance.LoadCore(node);
|
await CoreManager.Instance.LoadCore(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task AutoHideStartup()
|
|
||||||
{
|
|
||||||
if (_config.UiItem.AutoHideStartup)
|
|
||||||
{
|
|
||||||
AppEvents.ShowHideWindowRequested.Publish(false);
|
|
||||||
}
|
|
||||||
await Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion core job
|
#endregion core job
|
||||||
|
|
||||||
#region Presets
|
#region Presets
|
||||||
|
|
|
@ -52,11 +52,13 @@ public class ProfilesViewModel : MyReactiveObject
|
||||||
public ReactiveCommand<Unit, Unit> CopyServerCmd { get; }
|
public ReactiveCommand<Unit, Unit> CopyServerCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> SetDefaultServerCmd { get; }
|
public ReactiveCommand<Unit, Unit> SetDefaultServerCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> ShareServerCmd { get; }
|
public ReactiveCommand<Unit, Unit> ShareServerCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> SetDefaultMultipleServerXrayRandomCmd { get; }
|
public ReactiveCommand<Unit, Unit> GenGroupMultipleServerXrayRandomCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> SetDefaultMultipleServerXrayRoundRobinCmd { get; }
|
public ReactiveCommand<Unit, Unit> GenGroupMultipleServerXrayRoundRobinCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> SetDefaultMultipleServerXrayLeastPingCmd { get; }
|
public ReactiveCommand<Unit, Unit> GenGroupMultipleServerXrayLeastPingCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> SetDefaultMultipleServerXrayLeastLoadCmd { get; }
|
public ReactiveCommand<Unit, Unit> GenGroupMultipleServerXrayLeastLoadCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> SetDefaultMultipleServerSingBoxLeastPingCmd { get; }
|
public ReactiveCommand<Unit, Unit> GenGroupMultipleServerXrayFallbackCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> GenGroupMultipleServerSingBoxLeastPingCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> GenGroupMultipleServerSingBoxFallbackCmd { get; }
|
||||||
|
|
||||||
//servers move
|
//servers move
|
||||||
public ReactiveCommand<Unit, Unit> MoveTopCmd { get; }
|
public ReactiveCommand<Unit, Unit> MoveTopCmd { get; }
|
||||||
|
@ -138,25 +140,33 @@ public class ProfilesViewModel : MyReactiveObject
|
||||||
{
|
{
|
||||||
await ShareServerAsync();
|
await ShareServerAsync();
|
||||||
}, canEditRemove);
|
}, canEditRemove);
|
||||||
SetDefaultMultipleServerXrayRandomCmd = ReactiveCommand.CreateFromTask(async () =>
|
GenGroupMultipleServerXrayRandomCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
{
|
{
|
||||||
await SetDefaultMultipleServer(ECoreType.Xray, EMultipleLoad.Random);
|
await GenGroupMultipleServer(ECoreType.Xray, EMultipleLoad.Random);
|
||||||
}, canEditRemove);
|
}, canEditRemove);
|
||||||
SetDefaultMultipleServerXrayRoundRobinCmd = ReactiveCommand.CreateFromTask(async () =>
|
GenGroupMultipleServerXrayRoundRobinCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
{
|
{
|
||||||
await SetDefaultMultipleServer(ECoreType.Xray, EMultipleLoad.RoundRobin);
|
await GenGroupMultipleServer(ECoreType.Xray, EMultipleLoad.RoundRobin);
|
||||||
}, canEditRemove);
|
}, canEditRemove);
|
||||||
SetDefaultMultipleServerXrayLeastPingCmd = ReactiveCommand.CreateFromTask(async () =>
|
GenGroupMultipleServerXrayLeastPingCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
{
|
{
|
||||||
await SetDefaultMultipleServer(ECoreType.Xray, EMultipleLoad.LeastPing);
|
await GenGroupMultipleServer(ECoreType.Xray, EMultipleLoad.LeastPing);
|
||||||
}, canEditRemove);
|
}, canEditRemove);
|
||||||
SetDefaultMultipleServerXrayLeastLoadCmd = ReactiveCommand.CreateFromTask(async () =>
|
GenGroupMultipleServerXrayLeastLoadCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
{
|
{
|
||||||
await SetDefaultMultipleServer(ECoreType.Xray, EMultipleLoad.LeastLoad);
|
await GenGroupMultipleServer(ECoreType.Xray, EMultipleLoad.LeastLoad);
|
||||||
}, canEditRemove);
|
}, canEditRemove);
|
||||||
SetDefaultMultipleServerSingBoxLeastPingCmd = ReactiveCommand.CreateFromTask(async () =>
|
GenGroupMultipleServerXrayFallbackCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
{
|
{
|
||||||
await SetDefaultMultipleServer(ECoreType.sing_box, EMultipleLoad.LeastPing);
|
await GenGroupMultipleServer(ECoreType.Xray, EMultipleLoad.Fallback);
|
||||||
|
}, canEditRemove);
|
||||||
|
GenGroupMultipleServerSingBoxLeastPingCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
|
{
|
||||||
|
await GenGroupMultipleServer(ECoreType.sing_box, EMultipleLoad.LeastPing);
|
||||||
|
}, canEditRemove);
|
||||||
|
GenGroupMultipleServerSingBoxFallbackCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
|
{
|
||||||
|
await GenGroupMultipleServer(ECoreType.sing_box, EMultipleLoad.Fallback);
|
||||||
}, canEditRemove);
|
}, canEditRemove);
|
||||||
|
|
||||||
//servers move
|
//servers move
|
||||||
|
@ -500,6 +510,10 @@ public class ProfilesViewModel : MyReactiveObject
|
||||||
{
|
{
|
||||||
ret = await _updateView?.Invoke(EViewAction.AddServer2Window, item);
|
ret = await _updateView?.Invoke(EViewAction.AddServer2Window, item);
|
||||||
}
|
}
|
||||||
|
else if (eConfigType.IsGroupType())
|
||||||
|
{
|
||||||
|
ret = await _updateView?.Invoke(EViewAction.AddGroupServerWindow, item);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ret = await _updateView?.Invoke(EViewAction.AddServerWindow, item);
|
ret = await _updateView?.Invoke(EViewAction.AddServerWindow, item);
|
||||||
|
@ -615,7 +629,7 @@ public class ProfilesViewModel : MyReactiveObject
|
||||||
await _updateView?.Invoke(EViewAction.ShareServer, url);
|
await _updateView?.Invoke(EViewAction.ShareServer, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SetDefaultMultipleServer(ECoreType coreType, EMultipleLoad multipleLoad)
|
private async Task GenGroupMultipleServer(ECoreType coreType, EMultipleLoad multipleLoad)
|
||||||
{
|
{
|
||||||
var lstSelected = await GetProfileItems(true);
|
var lstSelected = await GetProfileItems(true);
|
||||||
if (lstSelected == null)
|
if (lstSelected == null)
|
||||||
|
@ -623,7 +637,7 @@ public class ProfilesViewModel : MyReactiveObject
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var ret = await ConfigHandler.AddCustomServer4Multiple(_config, lstSelected, coreType, multipleLoad);
|
var ret = await ConfigHandler.AddGroupServer4Multiple(_config, lstSelected, coreType, multipleLoad, SelectedSub?.Id);
|
||||||
if (ret.Success != true)
|
if (ret.Success != true)
|
||||||
{
|
{
|
||||||
NoticeManager.Instance.Enqueue(ResUI.OperationFailed);
|
NoticeManager.Instance.Enqueue(ResUI.OperationFailed);
|
||||||
|
@ -754,6 +768,18 @@ public class ProfilesViewModel : MyReactiveObject
|
||||||
NoticeManager.Instance.Enqueue(ResUI.PleaseSelectServer);
|
NoticeManager.Instance.Enqueue(ResUI.PleaseSelectServer);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var msgs = await ActionPrecheckManager.Instance.Check(item);
|
||||||
|
if (msgs.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (var msg in msgs)
|
||||||
|
{
|
||||||
|
NoticeManager.Instance.SendMessage(msg);
|
||||||
|
}
|
||||||
|
NoticeManager.Instance.Enqueue(Utils.List2String(msgs.Take(10).ToList(), true));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (blClipboard)
|
if (blClipboard)
|
||||||
{
|
{
|
||||||
var result = await CoreConfigHandler.GenerateClientConfig(item, null);
|
var result = await CoreConfigHandler.GenerateClientConfig(item, null);
|
||||||
|
|
|
@ -21,6 +21,9 @@ public class RoutingRuleDetailsViewModel : MyReactiveObject
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public string Process { get; set; }
|
public string Process { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public string? RuleType { get; set; }
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public bool AutoSort { get; set; }
|
public bool AutoSort { get; set; }
|
||||||
|
|
||||||
|
@ -51,6 +54,7 @@ public class RoutingRuleDetailsViewModel : MyReactiveObject
|
||||||
Domain = Utils.List2String(SelectedSource.Domain, true);
|
Domain = Utils.List2String(SelectedSource.Domain, true);
|
||||||
IP = Utils.List2String(SelectedSource.Ip, true);
|
IP = Utils.List2String(SelectedSource.Ip, true);
|
||||||
Process = Utils.List2String(SelectedSource.Process, true);
|
Process = Utils.List2String(SelectedSource.Process, true);
|
||||||
|
RuleType = SelectedSource.RuleType?.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SaveRulesAsync()
|
private async Task SaveRulesAsync()
|
||||||
|
@ -73,6 +77,7 @@ public class RoutingRuleDetailsViewModel : MyReactiveObject
|
||||||
}
|
}
|
||||||
SelectedSource.Protocol = ProtocolItems?.ToList();
|
SelectedSource.Protocol = ProtocolItems?.ToList();
|
||||||
SelectedSource.InboundTag = InboundTagItems?.ToList();
|
SelectedSource.InboundTag = InboundTagItems?.ToList();
|
||||||
|
SelectedSource.RuleType = RuleType.IsNullOrEmpty() ? null : (ERuleType)Enum.Parse(typeof(ERuleType), RuleType);
|
||||||
|
|
||||||
var hasRule = SelectedSource.Domain?.Count > 0
|
var hasRule = SelectedSource.Domain?.Count > 0
|
||||||
|| SelectedSource.Ip?.Count > 0
|
|| SelectedSource.Ip?.Count > 0
|
||||||
|
|
|
@ -107,13 +107,13 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
|
||||||
var it = new RulesItemModel()
|
var it = new RulesItemModel()
|
||||||
{
|
{
|
||||||
Id = item.Id,
|
Id = item.Id,
|
||||||
|
RuleTypeName = item.RuleType?.ToString(),
|
||||||
OutboundTag = item.OutboundTag,
|
OutboundTag = item.OutboundTag,
|
||||||
Port = item.Port,
|
Port = item.Port,
|
||||||
Network = item.Network,
|
Network = item.Network,
|
||||||
Protocols = Utils.List2String(item.Protocol),
|
Protocols = Utils.List2String(item.Protocol),
|
||||||
InboundTags = Utils.List2String(item.InboundTag),
|
InboundTags = Utils.List2String(item.InboundTag),
|
||||||
Domains = Utils.List2String(item.Domain),
|
Domains = Utils.List2String((item.Domain ?? []).Concat(item.Ip ?? []).ToList()),
|
||||||
Ips = Utils.List2String(item.Ip),
|
|
||||||
Enabled = item.Enabled,
|
Enabled = item.Enabled,
|
||||||
Remarks = item.Remarks,
|
Remarks = item.Remarks,
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
<ResourceDictionary>
|
<ResourceDictionary>
|
||||||
<ResourceDictionary.MergedDictionaries>
|
<ResourceDictionary.MergedDictionaries>
|
||||||
<ResourceInclude Source="Assets/GlobalResources.axaml" />
|
<ResourceInclude Source="Assets/GlobalResources.axaml" />
|
||||||
<ResourceInclude Source="Controls/AutoCompleteBox.axaml" />
|
|
||||||
</ResourceDictionary.MergedDictionaries>
|
</ResourceDictionary.MergedDictionaries>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</Application.Resources>
|
</Application.Resources>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
|
using Avalonia.Input.Platform;
|
||||||
using Avalonia.Media.Imaging;
|
using Avalonia.Media.Imaging;
|
||||||
using Avalonia.Platform;
|
using Avalonia.Platform;
|
||||||
|
|
||||||
|
@ -18,7 +19,7 @@ internal class AvaUtils
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return await clipboard.GetTextAsync();
|
return await clipboard.TryGetTextAsync();
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
@ -33,9 +34,7 @@ internal class AvaUtils
|
||||||
var clipboard = TopLevel.GetTopLevel(visual)?.Clipboard;
|
var clipboard = TopLevel.GetTopLevel(visual)?.Clipboard;
|
||||||
if (clipboard == null)
|
if (clipboard == null)
|
||||||
return;
|
return;
|
||||||
var dataObject = new DataObject();
|
await clipboard.SetTextAsync(strData);
|
||||||
dataObject.Set(DataFormats.Text, strData);
|
|
||||||
await clipboard.SetDataObjectAsync(dataObject);
|
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
<ResourceDictionary
|
|
||||||
xmlns="https://github.com/avaloniaui"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:controls="clr-namespace:v2rayN.Desktop.Controls">
|
|
||||||
<!-- Add Resources Here -->
|
|
||||||
<ControlTheme x:Key="{x:Type controls:AutoCompleteBox}" TargetType="controls:AutoCompleteBox">
|
|
||||||
<Setter Property="VerticalAlignment" Value="Center" />
|
|
||||||
<Setter Property="MinHeight" Value="{DynamicResource AutoCompleteBoxDefaultHeight}" />
|
|
||||||
<Setter Property="MaxDropDownHeight" Value="{DynamicResource AutoCompleteMaxDropdownHeight}" />
|
|
||||||
<Setter Property="Template">
|
|
||||||
<ControlTemplate TargetType="AutoCompleteBox">
|
|
||||||
<Panel>
|
|
||||||
<TextBox
|
|
||||||
Name="PART_TextBox"
|
|
||||||
MinHeight="{TemplateBinding MinHeight}"
|
|
||||||
VerticalAlignment="Stretch"
|
|
||||||
DataValidationErrors.Errors="{TemplateBinding (DataValidationErrors.Errors)}"
|
|
||||||
InnerLeftContent="{TemplateBinding InnerLeftContent}"
|
|
||||||
InnerRightContent="{TemplateBinding InnerRightContent}"
|
|
||||||
Watermark="{TemplateBinding Watermark}" />
|
|
||||||
<Popup
|
|
||||||
Name="PART_Popup"
|
|
||||||
MaxHeight="{TemplateBinding MaxDropDownHeight}"
|
|
||||||
IsLightDismissEnabled="True"
|
|
||||||
PlacementTarget="{TemplateBinding}">
|
|
||||||
<Border
|
|
||||||
MinWidth="{Binding Bounds.Width, RelativeSource={RelativeSource TemplatedParent}}"
|
|
||||||
Margin="{DynamicResource AutoCompleteBoxPopupMargin}"
|
|
||||||
Padding="{DynamicResource AutoCompleteBoxPopupPadding}"
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
Background="{DynamicResource AutoCompleteBoxPopupBackground}"
|
|
||||||
BorderBrush="{DynamicResource AutoCompleteBoxPopupBorderBrush}"
|
|
||||||
BorderThickness="{DynamicResource AutoCompleteBoxPopupBorderThickness}"
|
|
||||||
BoxShadow="{DynamicResource AutoCompleteBoxPopupBoxShadow}"
|
|
||||||
CornerRadius="{DynamicResource AutoCompleteBoxPopupCornerRadius}">
|
|
||||||
<ListBox
|
|
||||||
Name="PART_SelectingItemsControl"
|
|
||||||
Foreground="{TemplateBinding Foreground}"
|
|
||||||
ItemTemplate="{TemplateBinding ItemTemplate}"
|
|
||||||
ScrollViewer.HorizontalScrollBarVisibility="Auto"
|
|
||||||
ScrollViewer.VerticalScrollBarVisibility="Auto" />
|
|
||||||
</Border>
|
|
||||||
</Popup>
|
|
||||||
</Panel>
|
|
||||||
</ControlTemplate>
|
|
||||||
</Setter>
|
|
||||||
</ControlTheme>
|
|
||||||
</ResourceDictionary>
|
|
|
@ -1,40 +0,0 @@
|
||||||
using Avalonia.Input;
|
|
||||||
using Avalonia.Interactivity;
|
|
||||||
|
|
||||||
namespace v2rayN.Desktop.Controls;
|
|
||||||
|
|
||||||
public class AutoCompleteBox : Avalonia.Controls.AutoCompleteBox
|
|
||||||
{
|
|
||||||
static AutoCompleteBox()
|
|
||||||
{
|
|
||||||
MinimumPrefixLengthProperty.OverrideDefaultValue<AutoCompleteBox>(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AutoCompleteBox()
|
|
||||||
{
|
|
||||||
AddHandler(PointerPressedEvent, OnBoxPointerPressed, RoutingStrategies.Tunnel);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnBoxPointerPressed(object? sender, PointerPressedEventArgs e)
|
|
||||||
{
|
|
||||||
if (Equals(sender, this) && e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
|
|
||||||
{
|
|
||||||
SetCurrentValue(IsDropDownOpenProperty, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnGotFocus(GotFocusEventArgs e)
|
|
||||||
{
|
|
||||||
base.OnGotFocus(e);
|
|
||||||
if (IsDropDownOpen)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
SetCurrentValue(IsDropDownOpenProperty, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
SetCurrentValue(SelectedItemProperty, null);
|
|
||||||
}
|
|
||||||
}
|
|
151
v2rayN/v2rayN.Desktop/Views/AddGroupServerWindow.axaml
Normal file
151
v2rayN/v2rayN.Desktop/Views/AddGroupServerWindow.axaml
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
<Window
|
||||||
|
x:Class="v2rayN.Desktop.Views.AddGroupServerWindow"
|
||||||
|
xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
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"
|
||||||
|
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||||
|
Title="{x:Static resx:ResUI.menuServers}"
|
||||||
|
Width="900"
|
||||||
|
Height="700"
|
||||||
|
x:DataType="vms:AddGroupServerViewModel"
|
||||||
|
ShowInTaskbar="False"
|
||||||
|
WindowStartupLocation="CenterScreen"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
|
||||||
|
<DockPanel Margin="{StaticResource Margin8}">
|
||||||
|
<StackPanel
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
DockPanel.Dock="Bottom"
|
||||||
|
Orientation="Horizontal">
|
||||||
|
<Button
|
||||||
|
x:Name="btnSave"
|
||||||
|
Width="100"
|
||||||
|
Content="{x:Static resx:ResUI.TbConfirm}"
|
||||||
|
IsDefault="True" />
|
||||||
|
<Button
|
||||||
|
x:Name="btnCancel"
|
||||||
|
Width="100"
|
||||||
|
Margin="{StaticResource MarginLr8}"
|
||||||
|
Content="{x:Static resx:ResUI.TbCancel}"
|
||||||
|
IsCancel="True" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<Grid DockPanel.Dock="Top" RowDefinitions="Auto,*,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
|
||||||
|
|
||||||
|
<Grid
|
||||||
|
Grid.Row="0"
|
||||||
|
ColumnDefinitions="180,Auto,Auto"
|
||||||
|
RowDefinitions="Auto,Auto,Auto,Auto,Auto">
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
Text="{x:Static resx:ResUI.menuServers}" />
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{x:Static resx:ResUI.TbRemarks}" />
|
||||||
|
<TextBox
|
||||||
|
x:Name="txtRemarks"
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="1"
|
||||||
|
Width="400"
|
||||||
|
Margin="{StaticResource Margin4}" />
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="2"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{x:Static resx:ResUI.TbCoreType}" />
|
||||||
|
<ComboBox
|
||||||
|
x:Name="cmbCoreType"
|
||||||
|
Grid.Row="2"
|
||||||
|
Grid.Column="1"
|
||||||
|
Width="200"
|
||||||
|
Margin="{StaticResource Margin4}" />
|
||||||
|
|
||||||
|
<Grid
|
||||||
|
x:Name="gridPolicyGroup"
|
||||||
|
Grid.Row="3"
|
||||||
|
Grid.Column="0"
|
||||||
|
Grid.ColumnSpan="3"
|
||||||
|
ColumnDefinitions="180,Auto,Auto">
|
||||||
|
<TextBlock
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{x:Static resx:ResUI.TbPolicyGroupType}" />
|
||||||
|
<ComboBox
|
||||||
|
x:Name="cmbPolicyGroupType"
|
||||||
|
Grid.Column="1"
|
||||||
|
Width="200"
|
||||||
|
Margin="{StaticResource Margin4}" />
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<TabControl>
|
||||||
|
<TabItem HorizontalAlignment="Left" Header="{x:Static resx:ResUI.menuServerList}">
|
||||||
|
<DataGrid
|
||||||
|
x:Name="lstChild"
|
||||||
|
Grid.Row="1"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
Background="Transparent"
|
||||||
|
BorderThickness="1"
|
||||||
|
CanUserReorderColumns="False"
|
||||||
|
CanUserResizeColumns="True"
|
||||||
|
CanUserSortColumns="False"
|
||||||
|
GridLinesVisibility="All"
|
||||||
|
HeadersVisibility="Column"
|
||||||
|
IsReadOnly="True"
|
||||||
|
ItemsSource="{Binding ChildItemsObs}"
|
||||||
|
SelectionMode="Extended">
|
||||||
|
<DataGrid.ContextMenu>
|
||||||
|
<ContextMenu>
|
||||||
|
<MenuItem x:Name="menuAddChildServer" Header="{x:Static resx:ResUI.menuAddChildServer}" />
|
||||||
|
<MenuItem x:Name="menuRemoveChildServer" Header="{x:Static resx:ResUI.menuRemoveChildServer}" />
|
||||||
|
<MenuItem x:Name="menuSelectAllChild" Header="{x:Static resx:ResUI.menuSelectAll}" />
|
||||||
|
<Separator />
|
||||||
|
<MenuItem x:Name="menuMoveTop" Header="{x:Static resx:ResUI.menuMoveTop}" />
|
||||||
|
<MenuItem x:Name="menuMoveUp" Header="{x:Static resx:ResUI.menuMoveUp}" />
|
||||||
|
<MenuItem x:Name="menuMoveDown" Header="{x:Static resx:ResUI.menuMoveDown}" />
|
||||||
|
<MenuItem x:Name="menuMoveBottom" Header="{x:Static resx:ResUI.menuMoveBottom}" />
|
||||||
|
</ContextMenu>
|
||||||
|
</DataGrid.ContextMenu>
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="150"
|
||||||
|
Binding="{Binding ConfigType}"
|
||||||
|
Header="{x:Static resx:ResUI.LvServiceType}" />
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="150"
|
||||||
|
Binding="{Binding Remarks}"
|
||||||
|
Header="{x:Static resx:ResUI.LvRemarks}" />
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="120"
|
||||||
|
Binding="{Binding Address}"
|
||||||
|
Header="{x:Static resx:ResUI.LvAddress}" />
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="100"
|
||||||
|
Binding="{Binding Port}"
|
||||||
|
Header="{x:Static resx:ResUI.LvPort}" />
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="100"
|
||||||
|
Binding="{Binding Network}"
|
||||||
|
Header="{x:Static resx:ResUI.LvTransportProtocol}" />
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="100"
|
||||||
|
Binding="{Binding StreamSecurity}"
|
||||||
|
Header="{x:Static resx:ResUI.LvTLS}" />
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
</TabItem>
|
||||||
|
</TabControl>
|
||||||
|
</DockPanel>
|
||||||
|
</Window>
|
170
v2rayN/v2rayN.Desktop/Views/AddGroupServerWindow.axaml.cs
Normal file
170
v2rayN/v2rayN.Desktop/Views/AddGroupServerWindow.axaml.cs
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
using System.Reactive.Disposables;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Input;
|
||||||
|
using Avalonia.Interactivity;
|
||||||
|
using DynamicData;
|
||||||
|
using ReactiveUI;
|
||||||
|
using v2rayN.Desktop.Base;
|
||||||
|
|
||||||
|
namespace v2rayN.Desktop.Views;
|
||||||
|
|
||||||
|
public partial class AddGroupServerWindow : WindowBase<AddGroupServerViewModel>
|
||||||
|
{
|
||||||
|
public AddGroupServerWindow()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public AddGroupServerWindow(ProfileItem profileItem)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
this.Loaded += Window_Loaded;
|
||||||
|
btnCancel.Click += (s, e) => this.Close();
|
||||||
|
lstChild.SelectionChanged += LstChild_SelectionChanged;
|
||||||
|
|
||||||
|
ViewModel = new AddGroupServerViewModel(profileItem, UpdateViewHandler);
|
||||||
|
|
||||||
|
cmbCoreType.ItemsSource = Global.CoreTypes;
|
||||||
|
cmbPolicyGroupType.ItemsSource = new List<string>
|
||||||
|
{
|
||||||
|
ResUI.TbLeastPing,
|
||||||
|
ResUI.TbFallback,
|
||||||
|
ResUI.TbRandom,
|
||||||
|
ResUI.TbRoundRobin,
|
||||||
|
ResUI.TbLeastLoad,
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (profileItem.ConfigType)
|
||||||
|
{
|
||||||
|
case EConfigType.PolicyGroup:
|
||||||
|
this.Title = ResUI.TbConfigTypePolicyGroup;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EConfigType.ProxyChain:
|
||||||
|
this.Title = ResUI.TbConfigTypeProxyChain;
|
||||||
|
gridPolicyGroup.IsVisible = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.WhenActivated(disposables =>
|
||||||
|
{
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Remarks, v => v.txtRemarks.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.CoreType, v => v.cmbCoreType.SelectedValue).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.PolicyGroupType, v => v.cmbPolicyGroupType.SelectedValue).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.OneWayBind(ViewModel, vm => vm.ChildItemsObs, v => v.lstChild.ItemsSource).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedChild, v => v.lstChild.SelectedItem).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.BindCommand(ViewModel, vm => vm.RemoveCmd, v => v.menuRemoveChildServer).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.MoveTopCmd, v => v.menuMoveTop).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.MoveUpCmd, v => v.menuMoveUp).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.MoveDownCmd, v => v.menuMoveDown).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.MoveBottomCmd, v => v.menuMoveBottom).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Context menu actions that require custom logic (Add, SelectAll)
|
||||||
|
menuAddChildServer.Click += MenuAddChild_Click;
|
||||||
|
menuSelectAllChild.Click += (s, e) => lstChild.SelectAll();
|
||||||
|
|
||||||
|
// Keyboard shortcuts when focus is within grid
|
||||||
|
this.AddHandler(KeyDownEvent, AddGroupServerWindow_KeyDown, RoutingStrategies.Tunnel);
|
||||||
|
lstChild.LoadingRow += LstChild_LoadingRow;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LstChild_LoadingRow(object? sender, DataGridRowEventArgs e)
|
||||||
|
{
|
||||||
|
e.Row.Header = $" {e.Row.Index + 1}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
||||||
|
{
|
||||||
|
switch (action)
|
||||||
|
{
|
||||||
|
case EViewAction.CloseWindow:
|
||||||
|
this.Close(true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return await Task.FromResult(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Window_Loaded(object? sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
txtRemarks.Focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddGroupServerWindow_KeyDown(object? sender, KeyEventArgs e)
|
||||||
|
{
|
||||||
|
if (!lstChild.IsKeyboardFocusWithin)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ((e.KeyModifiers & (KeyModifiers.Control | KeyModifiers.Meta)) != 0)
|
||||||
|
{
|
||||||
|
if (e.Key == Key.A)
|
||||||
|
{
|
||||||
|
lstChild.SelectAll();
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (e.Key)
|
||||||
|
{
|
||||||
|
case Key.T:
|
||||||
|
ViewModel?.MoveServer(EMove.Top);
|
||||||
|
e.Handled = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Key.U:
|
||||||
|
ViewModel?.MoveServer(EMove.Up);
|
||||||
|
e.Handled = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Key.D:
|
||||||
|
ViewModel?.MoveServer(EMove.Down);
|
||||||
|
e.Handled = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Key.B:
|
||||||
|
ViewModel?.MoveServer(EMove.Bottom);
|
||||||
|
e.Handled = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Key.Delete:
|
||||||
|
ViewModel?.ChildRemoveAsync();
|
||||||
|
e.Handled = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void MenuAddChild_Click(object? sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var selectWindow = new ProfilesSelectWindow();
|
||||||
|
if (ViewModel?.SelectedSource?.ConfigType == EConfigType.PolicyGroup)
|
||||||
|
{
|
||||||
|
selectWindow.SetConfigTypeFilter(new[] { EConfigType.Custom }, exclude: true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
selectWindow.SetConfigTypeFilter(new[] { EConfigType.Custom, EConfigType.PolicyGroup, EConfigType.ProxyChain }, exclude: true);
|
||||||
|
}
|
||||||
|
selectWindow.AllowMultiSelect(true);
|
||||||
|
var result = await selectWindow.ShowDialog<bool?>(this);
|
||||||
|
if (result == true)
|
||||||
|
{
|
||||||
|
var profiles = await selectWindow.ProfileItems;
|
||||||
|
ViewModel?.ChildItemsObs.AddRange(profiles);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LstChild_SelectionChanged(object? sender, SelectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (ViewModel != null)
|
||||||
|
{
|
||||||
|
ViewModel.SelectedChildren = lstChild.SelectedItems.Cast<ProfileItem>().ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,6 @@
|
||||||
x:Class="v2rayN.Desktop.Views.DNSSettingWindow"
|
x:Class="v2rayN.Desktop.Views.DNSSettingWindow"
|
||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
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:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||||
|
@ -37,6 +36,7 @@
|
||||||
<TabItem Header="{x:Static resx:ResUI.ThBasicDNSSettings}">
|
<TabItem Header="{x:Static resx:ResUI.ThBasicDNSSettings}">
|
||||||
<ScrollViewer VerticalScrollBarVisibility="Visible">
|
<ScrollViewer VerticalScrollBarVisibility="Visible">
|
||||||
<Grid
|
<Grid
|
||||||
|
x:Name="gridBasicDNSSettings"
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
ColumnDefinitions="Auto,Auto,*"
|
ColumnDefinitions="Auto,Auto,*"
|
||||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
|
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
|
||||||
|
@ -55,13 +55,13 @@
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Text="{x:Static resx:ResUI.TbDomesticDNS}" />
|
Text="{x:Static resx:ResUI.TbDomesticDNS}" />
|
||||||
<ctrls:AutoCompleteBox
|
<ComboBox
|
||||||
x:Name="cmbDirectDNS"
|
x:Name="cmbDirectDNS"
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="300"
|
Width="300"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
Text="{Binding DirectDNS, Mode=TwoWay}" />
|
IsEditable="True" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
|
@ -69,117 +69,75 @@
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Text="{x:Static resx:ResUI.TbRemoteDNS}" />
|
Text="{x:Static resx:ResUI.TbRemoteDNS}" />
|
||||||
<ctrls:AutoCompleteBox
|
<ComboBox
|
||||||
x:Name="cmbRemoteDNS"
|
x:Name="cmbRemoteDNS"
|
||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="300"
|
Width="300"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
Text="{Binding RemoteDNS, Mode=TwoWay}" />
|
IsEditable="True" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="3"
|
Grid.Row="2"
|
||||||
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="300"
|
|
||||||
Margin="{StaticResource Margin4}"
|
|
||||||
Text="{Binding SingboxOutboundsResolveDNS, Mode=TwoWay}" />
|
|
||||||
<TextBlock
|
|
||||||
Grid.Row="3"
|
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Text="{x:Static resx:ResUI.TbSBOutboundDomainResolve}"
|
Text="{x:Static resx:ResUI.TbRemoteDNSTips}"
|
||||||
TextWrapping="Wrap" />
|
TextWrapping="Wrap" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="4"
|
Grid.Row="3"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Text="{x:Static resx:ResUI.TbSBDoHResolverServer}" />
|
Text="{x:Static resx:ResUI.TbXrayFreedomStrategy}" />
|
||||||
<ctrls:AutoCompleteBox
|
|
||||||
x:Name="cmbSBFinalResolverDNS"
|
|
||||||
Grid.Row="4"
|
|
||||||
Grid.Column="1"
|
|
||||||
Width="300"
|
|
||||||
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}"
|
|
||||||
TextWrapping="Wrap" />
|
|
||||||
|
|
||||||
<TextBlock
|
|
||||||
Grid.Row="5"
|
|
||||||
Grid.Column="0"
|
|
||||||
Margin="{StaticResource Margin4}"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Text="{x:Static resx:ResUI.TbXrayFreedomResolveStrategy}" />
|
|
||||||
<ComboBox
|
<ComboBox
|
||||||
x:Name="cmbRayFreedomDNSStrategy"
|
x:Name="cmbRayFreedomDNSStrategy"
|
||||||
Grid.Row="5"
|
Grid.Row="3"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="200"
|
Width="200"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
PlaceholderText="Default" />
|
PlaceholderText="Default" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="6"
|
Grid.Row="4"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Text="{x:Static resx:ResUI.TbSBDirectResolveStrategy}" />
|
Text="{x:Static resx:ResUI.TbSBDirectResolveStrategy}" />
|
||||||
<ComboBox
|
<ComboBox
|
||||||
x:Name="cmbSBDirectDNSStrategy"
|
x:Name="cmbSBDirectDNSStrategy"
|
||||||
Grid.Row="6"
|
Grid.Row="4"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="200"
|
Width="200"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
PlaceholderText="Default" />
|
PlaceholderText="Default" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="7"
|
Grid.Row="5"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Text="{x:Static resx:ResUI.TbSBRemoteResolveStrategy}" />
|
Text="{x:Static resx:ResUI.TbSBRemoteResolveStrategy}" />
|
||||||
<ComboBox
|
<ComboBox
|
||||||
x:Name="cmbSBRemoteDNSStrategy"
|
x:Name="cmbSBRemoteDNSStrategy"
|
||||||
Grid.Row="7"
|
Grid.Row="5"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="200"
|
Width="200"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
PlaceholderText="Default" />
|
PlaceholderText="Default" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="8"
|
Grid.Row="6"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Text="{x:Static resx:ResUI.TbAddCommonDNSHosts}" />
|
Text="{x:Static resx:ResUI.TbAddCommonDNSHosts}" />
|
||||||
<ToggleSwitch
|
<ToggleSwitch
|
||||||
x:Name="togAddCommonHosts"
|
x:Name="togAddCommonHosts"
|
||||||
Grid.Row="8"
|
Grid.Row="6"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
HorizontalAlignment="Left" />
|
HorizontalAlignment="Left" />
|
||||||
<TextBlock
|
|
||||||
Grid.Row="8"
|
|
||||||
Grid.Column="2"
|
|
||||||
Margin="{StaticResource Margin4}"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Text="{x:Static resx:ResUI.TbSBDoHOverride}"
|
|
||||||
TextWrapping="Wrap" />
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
@ -187,6 +145,7 @@
|
||||||
<TabItem Header="{x:Static resx:ResUI.ThAdvancedDNSSettings}">
|
<TabItem Header="{x:Static resx:ResUI.ThAdvancedDNSSettings}">
|
||||||
<ScrollViewer VerticalScrollBarVisibility="Visible">
|
<ScrollViewer VerticalScrollBarVisibility="Visible">
|
||||||
<Grid
|
<Grid
|
||||||
|
x:Name="gridAdvancedDNSSettings"
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
ColumnDefinitions="Auto,Auto,*"
|
ColumnDefinitions="Auto,Auto,*"
|
||||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,*">
|
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,*">
|
||||||
|
@ -258,13 +217,13 @@
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Text="{x:Static resx:ResUI.TbValidateDirectExpectedIPs}" />
|
Text="{x:Static resx:ResUI.TbValidateDirectExpectedIPs}" />
|
||||||
<ctrls:AutoCompleteBox
|
<ComboBox
|
||||||
x:Name="cmbDirectExpectedIPs"
|
x:Name="cmbDirectExpectedIPs"
|
||||||
Grid.Row="4"
|
Grid.Row="4"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="200"
|
Width="200"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
Text="{Binding DirectExpectedIPs, Mode=TwoWay}" />
|
IsEditable="True" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="4"
|
Grid.Row="4"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
|
@ -296,7 +255,7 @@
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
|
||||||
<TabItem Header="{x:Static resx:ResUI.TbSettingsCoreDns}">
|
<TabItem Header="{x:Static resx:ResUI.TbCustomDnsRay}">
|
||||||
<DockPanel Margin="{StaticResource Margin8}">
|
<DockPanel Margin="{StaticResource Margin8}">
|
||||||
<Grid DockPanel.Dock="Top">
|
<Grid DockPanel.Dock="Top">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
|
@ -361,11 +320,11 @@
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Text="{x:Static resx:ResUI.TbSettingsDomainDNSAddress}" />
|
Text="{x:Static resx:ResUI.TbSettingsDomainDNSAddress}" />
|
||||||
<ctrls:AutoCompleteBox
|
<ComboBox
|
||||||
x:Name="cmbdomainDNSAddressCompatible"
|
x:Name="cmbdomainDNSAddressCompatible"
|
||||||
Width="150"
|
Width="150"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
Text="{Binding DomainDNSAddressCompatible, Mode=TwoWay}" />
|
IsEditable="True" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</WrapPanel>
|
</WrapPanel>
|
||||||
|
|
||||||
|
@ -384,7 +343,7 @@
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
|
||||||
<TabItem Header="{x:Static resx:ResUI.TbSettingsCoreDnsSingbox}">
|
<TabItem Header="{x:Static resx:ResUI.TbCustomDnsSingbox}">
|
||||||
<DockPanel Margin="{StaticResource Margin8}">
|
<DockPanel Margin="{StaticResource Margin8}">
|
||||||
<Grid DockPanel.Dock="Top">
|
<Grid DockPanel.Dock="Top">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
|
@ -433,11 +392,11 @@
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Text="{x:Static resx:ResUI.TbSettingsDomainDNSAddress}" />
|
Text="{x:Static resx:ResUI.TbSettingsDomainDNSAddress}" />
|
||||||
<ctrls:AutoCompleteBox
|
<ComboBox
|
||||||
x:Name="cmbdomainDNSAddress2Compatible"
|
x:Name="cmbdomainDNSAddress2Compatible"
|
||||||
Width="150"
|
Width="150"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
Text="{Binding DomainDNSAddress2Compatible, Mode=TwoWay}" />
|
IsEditable="True" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</WrapPanel>
|
</WrapPanel>
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Reactive.Disposables;
|
using System.Reactive.Disposables;
|
||||||
|
using System.Reactive.Linq;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
|
@ -23,9 +24,7 @@ public partial class DNSSettingWindow : WindowBase<DNSSettingViewModel>
|
||||||
cmbSBDirectDNSStrategy.ItemsSource = Global.SingboxDomainStrategy4Out;
|
cmbSBDirectDNSStrategy.ItemsSource = Global.SingboxDomainStrategy4Out;
|
||||||
cmbSBRemoteDNSStrategy.ItemsSource = Global.SingboxDomainStrategy4Out;
|
cmbSBRemoteDNSStrategy.ItemsSource = Global.SingboxDomainStrategy4Out;
|
||||||
cmbDirectDNS.ItemsSource = Global.DomainDirectDNSAddress;
|
cmbDirectDNS.ItemsSource = Global.DomainDirectDNSAddress;
|
||||||
cmbSBResolverDNS.ItemsSource = Global.DomainDirectDNSAddress.Concat(new[] { "dhcp://auto,localhost" });
|
|
||||||
cmbRemoteDNS.ItemsSource = Global.DomainRemoteDNSAddress;
|
cmbRemoteDNS.ItemsSource = Global.DomainRemoteDNSAddress;
|
||||||
cmbSBFinalResolverDNS.ItemsSource = Global.DomainPureIPDNSAddress.Concat(new[] { "dhcp://auto,localhost" });
|
|
||||||
cmbDirectExpectedIPs.ItemsSource = Global.ExpectedIPs;
|
cmbDirectExpectedIPs.ItemsSource = Global.ExpectedIPs;
|
||||||
|
|
||||||
cmbdomainStrategy4FreedomCompatible.ItemsSource = Global.DomainStrategy4Freedoms;
|
cmbdomainStrategy4FreedomCompatible.ItemsSource = Global.DomainStrategy4Freedoms;
|
||||||
|
@ -39,15 +38,13 @@ public partial class DNSSettingWindow : WindowBase<DNSSettingViewModel>
|
||||||
this.Bind(ViewModel, vm => vm.AddCommonHosts, v => v.togAddCommonHosts.IsChecked).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.FakeIP, v => v.togFakeIP.IsChecked).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.BlockBindingQuery, v => v.togBlockBindingQuery.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.DirectDNS, v => v.cmbDirectDNS.Text).DisposeWith(disposables);
|
||||||
//this.Bind(ViewModel, vm => vm.RemoteDNS, v => v.cmbRemoteDNS.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.RayStrategy4Freedom, v => v.cmbRayFreedomDNSStrategy.SelectedItem).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SingboxStrategy4Direct, v => v.cmbSBDirectDNSStrategy.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.SingboxStrategy4Proxy, v => v.cmbSBRemoteDNSStrategy.SelectedItem).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.Hosts, v => v.txtHosts.Text).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.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.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
||||||
|
|
||||||
|
@ -56,27 +53,25 @@ public partial class DNSSettingWindow : WindowBase<DNSSettingViewModel>
|
||||||
|
|
||||||
this.Bind(ViewModel, vm => vm.UseSystemHostsCompatible, v => v.togUseSystemHostsCompatible.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.DomainStrategy4FreedomCompatible, v => v.cmbdomainStrategy4FreedomCompatible.SelectedItem).DisposeWith(disposables);
|
||||||
//this.Bind(ViewModel, vm => vm.DomainDNSAddressCompatible, v => v.cmbdomainDNSAddressCompatible.Text).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.NormalDNSCompatible, v => v.txtnormalDNSCompatible.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
this.Bind(ViewModel, vm => vm.DomainStrategy4Freedom2Compatible, v => v.cmbdomainStrategy4OutCompatible.SelectedItem).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.DomainDNSAddress2Compatible, v => v.cmbdomainDNSAddress2Compatible.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.NormalDNS2Compatible, v => v.txtnormalDNS2Compatible.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.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.ImportDefConfig4V2rayCompatibleCmd, v => v.btnImportDefConfig4V2rayCompatible).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.ImportDefConfig4SingboxCompatibleCmd, v => v.btnImportDefConfig4SingboxCompatible).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.ImportDefConfig4SingboxCompatibleCmd, v => v.btnImportDefConfig4SingboxCompatible).DisposeWith(disposables);
|
||||||
|
|
||||||
this.WhenAnyValue(
|
this.WhenAnyValue(x => x.ViewModel.IsSimpleDNSEnabled)
|
||||||
x => x.ViewModel.RayCustomDNSEnableCompatible,
|
.Select(b => !b)
|
||||||
x => x.ViewModel.SBCustomDNSEnableCompatible,
|
.BindTo(this.FindControl<TextBlock>("txtBasicDNSSettingsInvalid"), t => t.IsVisible);
|
||||||
(ray, sb) => ray && sb
|
this.WhenAnyValue(x => x.ViewModel.IsSimpleDNSEnabled)
|
||||||
).BindTo(this.FindControl<TextBlock>("txtBasicDNSSettingsInvalid"), t => t.IsVisible);
|
.Select(b => !b)
|
||||||
this.WhenAnyValue(
|
.BindTo(this.FindControl<TextBlock>("txtAdvancedDNSSettingsInvalid"), t => t.IsVisible);
|
||||||
x => x.ViewModel.RayCustomDNSEnableCompatible,
|
this.Bind(ViewModel, vm => vm.IsSimpleDNSEnabled, v => v.gridBasicDNSSettings.IsEnabled).DisposeWith(disposables);
|
||||||
x => x.ViewModel.SBCustomDNSEnableCompatible,
|
this.Bind(ViewModel, vm => vm.IsSimpleDNSEnabled, v => v.gridAdvancedDNSSettings.IsEnabled).DisposeWith(disposables);
|
||||||
(ray, sb) => ray && sb
|
|
||||||
).BindTo(this.FindControl<TextBlock>("txtAdvancedDNSSettingsInvalid"), t => t.IsVisible);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,8 @@
|
||||||
<MenuItem x:Name="menuAddServerViaScan" Header="{x:Static resx:ResUI.menuAddServerViaScan}" />
|
<MenuItem x:Name="menuAddServerViaScan" Header="{x:Static resx:ResUI.menuAddServerViaScan}" />
|
||||||
<MenuItem x:Name="menuAddServerViaImage" Header="{x:Static resx:ResUI.menuAddServerViaImage}" />
|
<MenuItem x:Name="menuAddServerViaImage" Header="{x:Static resx:ResUI.menuAddServerViaImage}" />
|
||||||
<MenuItem x:Name="menuAddCustomServer" Header="{x:Static resx:ResUI.menuAddCustomServer}" />
|
<MenuItem x:Name="menuAddCustomServer" Header="{x:Static resx:ResUI.menuAddCustomServer}" />
|
||||||
|
<MenuItem x:Name="menuAddPolicyGroupServer" Header="{x:Static resx:ResUI.menuAddPolicyGroupServer}" />
|
||||||
|
<MenuItem x:Name="menuAddProxyChainServer" Header="{x:Static resx:ResUI.menuAddProxyChainServer}" />
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuItem x:Name="menuAddVmessServer" Header="{x:Static resx:ResUI.menuAddVmessServer}" />
|
<MenuItem x:Name="menuAddVmessServer" Header="{x:Static resx:ResUI.menuAddVmessServer}" />
|
||||||
<MenuItem x:Name="menuAddVlessServer" Header="{x:Static resx:ResUI.menuAddVlessServer}" />
|
<MenuItem x:Name="menuAddVlessServer" Header="{x:Static resx:ResUI.menuAddVlessServer}" />
|
||||||
|
|
|
@ -83,6 +83,8 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
|
||||||
this.BindCommand(ViewModel, vm => vm.AddWireguardServerCmd, v => v.menuAddWireguardServer).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.AddAnytlsServerCmd, v => v.menuAddAnytlsServer).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.AddCustomServerCmd, v => v.menuAddCustomServer).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.AddCustomServerCmd, v => v.menuAddCustomServer).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.AddPolicyGroupServerCmd, v => v.menuAddPolicyGroupServer).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.AddProxyChainServerCmd, v => v.menuAddProxyChainServer).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.AddServerViaClipboardCmd, v => v.menuAddServerViaClipboard).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.AddServerViaClipboardCmd, v => v.menuAddServerViaClipboard).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.AddServerViaScanCmd, v => v.menuAddServerViaScan).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.AddServerViaScanCmd, v => v.menuAddServerViaScan).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.AddServerViaImageCmd, v => v.menuAddServerViaImage).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.AddServerViaImageCmd, v => v.menuAddServerViaImage).DisposeWith(disposables);
|
||||||
|
@ -176,6 +178,11 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
|
||||||
}
|
}
|
||||||
menuAddServerViaScan.IsVisible = false;
|
menuAddServerViaScan.IsVisible = false;
|
||||||
|
|
||||||
|
if (_config.UiItem.AutoHideStartup)
|
||||||
|
{
|
||||||
|
this.WindowState = WindowState.Minimized;
|
||||||
|
}
|
||||||
|
|
||||||
AddHelpMenuItem();
|
AddHelpMenuItem();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,6 +214,11 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
|
||||||
return false;
|
return false;
|
||||||
return await new AddServer2Window((ProfileItem)obj).ShowDialog<bool>(this);
|
return await new AddServer2Window((ProfileItem)obj).ShowDialog<bool>(this);
|
||||||
|
|
||||||
|
case EViewAction.AddGroupServerWindow:
|
||||||
|
if (obj is null)
|
||||||
|
return false;
|
||||||
|
return await new AddGroupServerWindow((ProfileItem)obj).ShowDialog<bool>(this);
|
||||||
|
|
||||||
case EViewAction.DNSSettingWindow:
|
case EViewAction.DNSSettingWindow:
|
||||||
return await new DNSSettingWindow().ShowDialog<bool>(this);
|
return await new DNSSettingWindow().ShowDialog<bool>(this);
|
||||||
|
|
||||||
|
@ -399,7 +411,10 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
|
||||||
|
|
||||||
public void ShowHideWindow(bool? blShow)
|
public void ShowHideWindow(bool? blShow)
|
||||||
{
|
{
|
||||||
var bl = blShow ?? (!_config.UiItem.ShowInTaskbar ^ (WindowState == WindowState.Minimized));
|
var bl = blShow ??
|
||||||
|
Utils.IsLinux()
|
||||||
|
? (!_config.UiItem.ShowInTaskbar ^ (WindowState == WindowState.Minimized))
|
||||||
|
: !_config.UiItem.ShowInTaskbar;
|
||||||
if (bl)
|
if (bl)
|
||||||
{
|
{
|
||||||
this.Show();
|
this.Show();
|
||||||
|
@ -431,6 +446,10 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
|
||||||
protected override void OnLoaded(object? sender, RoutedEventArgs e)
|
protected override void OnLoaded(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnLoaded(sender, e);
|
base.OnLoaded(sender, e);
|
||||||
|
if (_config.UiItem.AutoHideStartup)
|
||||||
|
{
|
||||||
|
ShowHideWindow(false);
|
||||||
|
}
|
||||||
RestoreUI();
|
RestoreUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
x:Class="v2rayN.Desktop.Views.OptionSettingWindow"
|
x:Class="v2rayN.Desktop.Views.OptionSettingWindow"
|
||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
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:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||||
|
@ -356,14 +355,15 @@
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
HorizontalAlignment="Left" />
|
HorizontalAlignment="Left" />
|
||||||
<!--
|
<TextBlock
|
||||||
<TextBlock
|
x:Name="tbAutoRunTip"
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
VerticalAlignment="Center"
|
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
Text="{x:Static resx:ResUI.TbSettingsStartBootTip}"
|
Text="{x:Static resx:ResUI.TbSettingsStartBootTip}"
|
||||||
TextWrapping="Wrap" />-->
|
TextWrapping="Wrap" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
|
@ -468,6 +468,13 @@
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
HorizontalAlignment="Left" />
|
HorizontalAlignment="Left" />
|
||||||
|
<TextBlock
|
||||||
|
x:Name="labHide2TrayWhenCloseTip"
|
||||||
|
Grid.Row="9"
|
||||||
|
Grid.Column="2"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{x:Static resx:ResUI.TbSettingsHide2TrayWhenCloseTip}" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="11"
|
Grid.Row="11"
|
||||||
|
@ -502,12 +509,13 @@
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Text="{x:Static resx:ResUI.TbSettingsCurrentFontFamily}" />
|
Text="{x:Static resx:ResUI.TbSettingsCurrentFontFamily}" />
|
||||||
<ctrls:AutoCompleteBox
|
<ComboBox
|
||||||
x:Name="cmbcurrentFontFamily"
|
x:Name="cmbcurrentFontFamily"
|
||||||
Grid.Row="15"
|
Grid.Row="15"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="300"
|
Width="300"
|
||||||
Margin="{StaticResource Margin4}" />
|
Margin="{StaticResource Margin4}"
|
||||||
|
IsEditable="True" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="15"
|
Grid.Row="15"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
|
@ -548,12 +556,13 @@
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Text="{x:Static resx:ResUI.TbSettingsSpeedTestUrl}" />
|
Text="{x:Static resx:ResUI.TbSettingsSpeedTestUrl}" />
|
||||||
<ctrls:AutoCompleteBox
|
<ComboBox
|
||||||
Name="cmbSpeedTestUrl"
|
Name="cmbSpeedTestUrl"
|
||||||
Grid.Row="18"
|
Grid.Row="18"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="300"
|
Width="300"
|
||||||
Margin="{StaticResource Margin4}" />
|
Margin="{StaticResource Margin4}"
|
||||||
|
IsEditable="True" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="19"
|
Grid.Row="19"
|
||||||
|
@ -561,12 +570,13 @@
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Text="{x:Static resx:ResUI.TbSettingsSpeedPingTestUrl}" />
|
Text="{x:Static resx:ResUI.TbSettingsSpeedPingTestUrl}" />
|
||||||
<ctrls:AutoCompleteBox
|
<ComboBox
|
||||||
x:Name="cmbSpeedPingTestUrl"
|
x:Name="cmbSpeedPingTestUrl"
|
||||||
Grid.Row="19"
|
Grid.Row="19"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="300"
|
Width="300"
|
||||||
Margin="{StaticResource Margin4}" />
|
Margin="{StaticResource Margin4}"
|
||||||
|
IsEditable="True" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="20"
|
Grid.Row="20"
|
||||||
|
@ -587,12 +597,13 @@
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Text="{x:Static resx:ResUI.TbSettingsSubConvert}" />
|
Text="{x:Static resx:ResUI.TbSettingsSubConvert}" />
|
||||||
<ctrls:AutoCompleteBox
|
<ComboBox
|
||||||
x:Name="cmbSubConvertUrl"
|
x:Name="cmbSubConvertUrl"
|
||||||
Grid.Row="21"
|
Grid.Row="21"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="300"
|
Width="300"
|
||||||
Margin="{StaticResource Margin4}" />
|
Margin="{StaticResource Margin4}"
|
||||||
|
IsEditable="True" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="22"
|
Grid.Row="22"
|
||||||
|
@ -618,7 +629,8 @@
|
||||||
Grid.Row="23"
|
Grid.Row="23"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="300"
|
Width="300"
|
||||||
Margin="{StaticResource Margin4}" />
|
Margin="{StaticResource Margin4}"
|
||||||
|
IsEditable="True" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="23"
|
Grid.Row="23"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
|
@ -638,7 +650,8 @@
|
||||||
Grid.Row="24"
|
Grid.Row="24"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="300"
|
Width="300"
|
||||||
Margin="{StaticResource Margin4}" />
|
Margin="{StaticResource Margin4}"
|
||||||
|
IsEditable="True" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="24"
|
Grid.Row="24"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
|
@ -658,7 +671,8 @@
|
||||||
Grid.Row="25"
|
Grid.Row="25"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="300"
|
Width="300"
|
||||||
Margin="{StaticResource Margin4}" />
|
Margin="{StaticResource Margin4}"
|
||||||
|
IsEditable="True" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="25"
|
Grid.Row="25"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
|
|
|
@ -131,19 +131,25 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
|
||||||
|
|
||||||
labHide2TrayWhenClose.IsVisible = false;
|
labHide2TrayWhenClose.IsVisible = false;
|
||||||
togHide2TrayWhenClose.IsVisible = false;
|
togHide2TrayWhenClose.IsVisible = false;
|
||||||
|
labHide2TrayWhenCloseTip.IsVisible = false;
|
||||||
}
|
}
|
||||||
else if (Utils.IsLinux())
|
else if (Utils.IsLinux())
|
||||||
{
|
{
|
||||||
txbSettingsExceptionTip.IsVisible = false;
|
txbSettingsExceptionTip.IsVisible = false;
|
||||||
panSystemProxyAdvanced.IsVisible = false;
|
panSystemProxyAdvanced.IsVisible = false;
|
||||||
|
|
||||||
|
tbAutoRunTip.IsVisible = false;
|
||||||
}
|
}
|
||||||
else if (Utils.IsOSX())
|
else if (Utils.IsOSX())
|
||||||
{
|
{
|
||||||
txbSettingsExceptionTip.IsVisible = false;
|
txbSettingsExceptionTip.IsVisible = false;
|
||||||
panSystemProxyAdvanced.IsVisible = false;
|
panSystemProxyAdvanced.IsVisible = false;
|
||||||
|
|
||||||
|
tbAutoRunTip.IsVisible = false;
|
||||||
|
|
||||||
labHide2TrayWhenClose.IsVisible = false;
|
labHide2TrayWhenClose.IsVisible = false;
|
||||||
togHide2TrayWhenClose.IsVisible = false;
|
togHide2TrayWhenClose.IsVisible = false;
|
||||||
|
labHide2TrayWhenCloseTip.IsVisible = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -99,13 +99,14 @@
|
||||||
<MenuItem x:Name="menuCopyServer" Header="{x:Static resx:ResUI.menuCopyServer}" />
|
<MenuItem x:Name="menuCopyServer" Header="{x:Static resx:ResUI.menuCopyServer}" />
|
||||||
<MenuItem x:Name="menuShareServer" Header="{x:Static resx:ResUI.menuShareServer}" />
|
<MenuItem x:Name="menuShareServer" Header="{x:Static resx:ResUI.menuShareServer}" />
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuItem Header="{x:Static resx:ResUI.menuSetDefaultMultipleServer}">
|
<MenuItem Header="{x:Static resx:ResUI.menuGenGroupMultipleServer}">
|
||||||
<MenuItem x:Name="menuSetDefaultMultipleServerXrayRandom" Header="{x:Static resx:ResUI.menuSetDefaultMultipleServerXrayRandom}" />
|
<MenuItem x:Name="menuGenGroupMultipleServerXrayRandom" Header="{x:Static resx:ResUI.menuGenGroupMultipleServerXrayRandom}" />
|
||||||
<MenuItem x:Name="menuSetDefaultMultipleServerXrayRoundRobin" Header="{x:Static resx:ResUI.menuSetDefaultMultipleServerXrayRoundRobin}" />
|
<MenuItem x:Name="menuGenGroupMultipleServerXrayRoundRobin" Header="{x:Static resx:ResUI.menuGenGroupMultipleServerXrayRoundRobin}" />
|
||||||
<MenuItem x:Name="menuSetDefaultMultipleServerXrayLeastPing" Header="{x:Static resx:ResUI.menuSetDefaultMultipleServerXrayLeastPing}" />
|
<MenuItem x:Name="menuGenGroupMultipleServerXrayLeastPing" Header="{x:Static resx:ResUI.menuGenGroupMultipleServerXrayLeastPing}" />
|
||||||
<MenuItem x:Name="menuSetDefaultMultipleServerXrayLeastLoad" Header="{x:Static resx:ResUI.menuSetDefaultMultipleServerXrayLeastLoad}" />
|
<MenuItem x:Name="menuGenGroupMultipleServerXrayLeastLoad" Header="{x:Static resx:ResUI.menuGenGroupMultipleServerXrayLeastLoad}" />
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuItem x:Name="menuSetDefaultMultipleServerSingBoxLeastPing" Header="{x:Static resx:ResUI.menuSetDefaultMultipleServerSingBoxLeastPing}" />
|
<MenuItem x:Name="menuGenGroupMultipleServerSingBoxLeastPing" Header="{x:Static resx:ResUI.menuGenGroupMultipleServerSingBoxLeastPing}" />
|
||||||
|
<MenuItem x:Name="menuGenGroupMultipleServerSingBoxFallback" Header="{x:Static resx:ResUI.menuGenGroupMultipleServerSingBoxFallback}" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuItem x:Name="menuMixedTestServer" Header="{x:Static resx:ResUI.menuMixedTestServer}" />
|
<MenuItem x:Name="menuMixedTestServer" Header="{x:Static resx:ResUI.menuMixedTestServer}" />
|
||||||
|
|
|
@ -66,11 +66,12 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
|
||||||
this.BindCommand(ViewModel, vm => vm.CopyServerCmd, v => v.menuCopyServer).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.CopyServerCmd, v => v.menuCopyServer).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.SetDefaultServerCmd, v => v.menuSetDefaultServer).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.SetDefaultServerCmd, v => v.menuSetDefaultServer).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.ShareServerCmd, v => v.menuShareServer).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.ShareServerCmd, v => v.menuShareServer).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.SetDefaultMultipleServerXrayRandomCmd, v => v.menuSetDefaultMultipleServerXrayRandom).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerXrayRandomCmd, v => v.menuGenGroupMultipleServerXrayRandom).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.SetDefaultMultipleServerXrayRoundRobinCmd, v => v.menuSetDefaultMultipleServerXrayRoundRobin).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerXrayRoundRobinCmd, v => v.menuGenGroupMultipleServerXrayRoundRobin).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.SetDefaultMultipleServerXrayLeastPingCmd, v => v.menuSetDefaultMultipleServerXrayLeastPing).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerXrayLeastPingCmd, v => v.menuGenGroupMultipleServerXrayLeastPing).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.SetDefaultMultipleServerXrayLeastLoadCmd, v => v.menuSetDefaultMultipleServerXrayLeastLoad).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerXrayLeastLoadCmd, v => v.menuGenGroupMultipleServerXrayLeastLoad).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.SetDefaultMultipleServerSingBoxLeastPingCmd, v => v.menuSetDefaultMultipleServerSingBoxLeastPing).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerSingBoxLeastPingCmd, v => v.menuGenGroupMultipleServerSingBoxLeastPing).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerSingBoxFallbackCmd, v => v.menuGenGroupMultipleServerSingBoxFallback).DisposeWith(disposables);
|
||||||
|
|
||||||
//servers move
|
//servers move
|
||||||
//this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.cmbMoveToGroup.ItemsSource).DisposeWith(disposables);
|
//this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.cmbMoveToGroup.ItemsSource).DisposeWith(disposables);
|
||||||
|
@ -167,6 +168,11 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
|
||||||
return false;
|
return false;
|
||||||
return await new AddServer2Window((ProfileItem)obj).ShowDialog<bool>(_window);
|
return await new AddServer2Window((ProfileItem)obj).ShowDialog<bool>(_window);
|
||||||
|
|
||||||
|
case EViewAction.AddGroupServerWindow:
|
||||||
|
if (obj is null)
|
||||||
|
return false;
|
||||||
|
return await new AddGroupServerWindow((ProfileItem)obj).ShowDialog<bool>(_window);
|
||||||
|
|
||||||
case EViewAction.ShareServer:
|
case EViewAction.ShareServer:
|
||||||
if (obj is null)
|
if (obj is null)
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
x:Class="v2rayN.Desktop.Views.RoutingRuleDetailsWindow"
|
x:Class="v2rayN.Desktop.Views.RoutingRuleDetailsWindow"
|
||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
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:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||||
|
@ -19,7 +18,7 @@
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
ColumnDefinitions="Auto,Auto,Auto"
|
ColumnDefinitions="Auto,Auto,Auto"
|
||||||
DockPanel.Dock="Top"
|
DockPanel.Dock="Top"
|
||||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto">
|
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
|
@ -33,59 +32,85 @@
|
||||||
Width="300"
|
Width="300"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
HorizontalAlignment="Left" />
|
HorizontalAlignment="Left" />
|
||||||
<ToggleSwitch
|
<StackPanel
|
||||||
x:Name="togEnabled"
|
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
VerticalAlignment="Center" />
|
VerticalAlignment="Center"
|
||||||
|
Orientation="Horizontal">
|
||||||
|
<ToggleSwitch
|
||||||
|
x:Name="togEnabled"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Text="outboundTag" />
|
Text="{x:Static resx:ResUI.TbRuleType}" />
|
||||||
<ctrls:AutoCompleteBox
|
<ComboBox
|
||||||
Name="cmbOutboundTag"
|
x:Name="cmbRuleType"
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="300"
|
Width="300"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
Text="{Binding SelectedSource.OutboundTag, Mode=TwoWay}" />
|
MaxDropDownHeight="1000" />
|
||||||
<StackPanel
|
<TextBlock
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
Orientation="Horizontal"
|
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
VerticalAlignment="Center">
|
VerticalAlignment="Center"
|
||||||
<Button
|
Text="{x:Static resx:ResUI.TbRuleTypeTips}" />
|
||||||
x:Name="btnSelectProfile"
|
|
||||||
Margin="0,0,8,0"
|
|
||||||
Content="{x:Static resx:ResUI.TbSelectProfile}"
|
|
||||||
Click="BtnSelectProfile_Click" />
|
|
||||||
<TextBlock
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Text="{x:Static resx:ResUI.TbRuleOutboundTagTip}" />
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
|
Text="outboundTag" />
|
||||||
|
<ComboBox
|
||||||
|
Name="cmbOutboundTag"
|
||||||
|
Grid.Row="2"
|
||||||
|
Grid.Column="1"
|
||||||
|
Width="300"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
IsEditable="True" />
|
||||||
|
<StackPanel
|
||||||
|
Grid.Row="2"
|
||||||
|
Grid.Column="2"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Orientation="Horizontal">
|
||||||
|
<Button
|
||||||
|
x:Name="btnSelectProfile"
|
||||||
|
Margin="0,0,8,0"
|
||||||
|
Click="BtnSelectProfile_Click"
|
||||||
|
Content="{x:Static resx:ResUI.TbSelectProfile}" />
|
||||||
|
<TextBlock VerticalAlignment="Center" Text="{x:Static resx:ResUI.TbRuleOutboundTagTip}" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="3"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
Text="port" />
|
Text="port" />
|
||||||
<TextBox
|
<TextBox
|
||||||
x:Name="txtPort"
|
x:Name="txtPort"
|
||||||
Grid.Row="2"
|
Grid.Row="3"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="300"
|
Width="300"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
HorizontalAlignment="Left" />
|
HorizontalAlignment="Left" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="2"
|
Grid.Row="3"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
|
@ -93,21 +118,21 @@
|
||||||
Text="{x:Static resx:ResUI.TbRuleMatchingTips}" />
|
Text="{x:Static resx:ResUI.TbRuleMatchingTips}" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="3"
|
Grid.Row="4"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Text="protocol" />
|
Text="protocol" />
|
||||||
<ListBox
|
<ListBox
|
||||||
x:Name="clbProtocol"
|
x:Name="clbProtocol"
|
||||||
Grid.Row="3"
|
Grid.Row="4"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
SelectionMode="Multiple,Toggle"
|
SelectionMode="Multiple,Toggle"
|
||||||
Theme="{DynamicResource CardCheckGroupListBox}" />
|
Theme="{DynamicResource CardCheckGroupListBox}" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="3"
|
Grid.Row="4"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center">
|
VerticalAlignment="Center">
|
||||||
|
@ -117,20 +142,20 @@
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="4"
|
Grid.Row="5"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Text="inboundTag" />
|
Text="inboundTag" />
|
||||||
<ListBox
|
<ListBox
|
||||||
x:Name="clbInboundTag"
|
x:Name="clbInboundTag"
|
||||||
Grid.Row="4"
|
Grid.Row="5"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
SelectionMode="Multiple,Toggle"
|
SelectionMode="Multiple,Toggle"
|
||||||
Theme="{DynamicResource CardCheckGroupListBox}" />
|
Theme="{DynamicResource CardCheckGroupListBox}" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="4"
|
Grid.Row="5"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
|
@ -138,20 +163,20 @@
|
||||||
Text="{x:Static resx:ResUI.TbRoutingInboundTagTips}" />
|
Text="{x:Static resx:ResUI.TbRoutingInboundTagTips}" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="5"
|
Grid.Row="6"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Text="network" />
|
Text="network" />
|
||||||
<ComboBox
|
<ComboBox
|
||||||
x:Name="cmbNetwork"
|
x:Name="cmbNetwork"
|
||||||
Grid.Row="5"
|
Grid.Row="6"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="300"
|
Width="300"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
MaxDropDownHeight="1000" />
|
MaxDropDownHeight="1000" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="5"
|
Grid.Row="6"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
|
|
|
@ -28,6 +28,7 @@ public partial class RoutingRuleDetailsWindow : WindowBase<RoutingRuleDetailsVie
|
||||||
clbProtocol.ItemsSource = Global.RuleProtocols;
|
clbProtocol.ItemsSource = Global.RuleProtocols;
|
||||||
clbInboundTag.ItemsSource = Global.InboundTags;
|
clbInboundTag.ItemsSource = Global.InboundTags;
|
||||||
cmbNetwork.ItemsSource = Global.RuleNetworks;
|
cmbNetwork.ItemsSource = Global.RuleNetworks;
|
||||||
|
cmbRuleType.ItemsSource = Utils.GetEnumNames<ERuleType>().AppendEmpty();
|
||||||
|
|
||||||
if (!rulesItem.Id.IsNullOrEmpty())
|
if (!rulesItem.Id.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
|
@ -43,6 +44,7 @@ public partial class RoutingRuleDetailsWindow : WindowBase<RoutingRuleDetailsVie
|
||||||
|
|
||||||
this.WhenActivated(disposables =>
|
this.WhenActivated(disposables =>
|
||||||
{
|
{
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.OutboundTag, v => v.cmbOutboundTag.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Remarks, v => v.txtRemarks.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Remarks, v => v.txtRemarks.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.OutboundTag, v => v.cmbOutboundTag.Text).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.Port, v => v.txtPort.Text).DisposeWith(disposables);
|
||||||
|
@ -52,6 +54,7 @@ public partial class RoutingRuleDetailsWindow : WindowBase<RoutingRuleDetailsVie
|
||||||
this.Bind(ViewModel, vm => vm.IP, v => v.txtIP.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.IP, v => v.txtIP.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.Process, v => v.txtProcess.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.Process, v => v.txtProcess.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.AutoSort, v => v.chkAutoSort.IsChecked).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.AutoSort, v => v.chkAutoSort.IsChecked).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.RuleType, v => v.cmbRuleType.SelectedValue).DisposeWith(disposables);
|
||||||
|
|
||||||
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
||||||
});
|
});
|
||||||
|
|
|
@ -209,7 +209,11 @@
|
||||||
Binding="{Binding Remarks}"
|
Binding="{Binding Remarks}"
|
||||||
Header="{x:Static resx:ResUI.LvRemarks}" />
|
Header="{x:Static resx:ResUI.LvRemarks}" />
|
||||||
<DataGridTextColumn
|
<DataGridTextColumn
|
||||||
Width="120"
|
Width="100"
|
||||||
|
Binding="{Binding RuleTypeName}"
|
||||||
|
Header="{x:Static resx:ResUI.TbRuleType}" />
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="130"
|
||||||
Binding="{Binding OutboundTag}"
|
Binding="{Binding OutboundTag}"
|
||||||
Header="outboundTag" />
|
Header="outboundTag" />
|
||||||
<DataGridTextColumn
|
<DataGridTextColumn
|
||||||
|
@ -225,17 +229,13 @@
|
||||||
Binding="{Binding InboundTags}"
|
Binding="{Binding InboundTags}"
|
||||||
Header="inboundTag" />
|
Header="inboundTag" />
|
||||||
<DataGridTextColumn
|
<DataGridTextColumn
|
||||||
Width="90"
|
Width="100"
|
||||||
Binding="{Binding Network}"
|
Binding="{Binding Network}"
|
||||||
Header="network" />
|
Header="network" />
|
||||||
<DataGridTextColumn
|
<DataGridTextColumn
|
||||||
Width="*"
|
Width="*"
|
||||||
Binding="{Binding Domains}"
|
Binding="{Binding Domains}"
|
||||||
Header="domain" />
|
Header="domain / ip" />
|
||||||
<DataGridTextColumn
|
|
||||||
Width="*"
|
|
||||||
Binding="{Binding Ips}"
|
|
||||||
Header="ip" />
|
|
||||||
</DataGrid.Columns>
|
</DataGrid.Columns>
|
||||||
</DataGrid>
|
</DataGrid>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
Width="{StaticResource IconButtonWidth}"
|
Width="{StaticResource IconButtonWidth}"
|
||||||
Height="{StaticResource IconButtonHeight}"
|
Height="{StaticResource IconButtonHeight}"
|
||||||
Margin="{StaticResource MarginLr8}"
|
Margin="{StaticResource MarginLr8}"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
Theme="{DynamicResource BorderlessButton}">
|
Theme="{DynamicResource BorderlessButton}">
|
||||||
<Button.Content>
|
<Button.Content>
|
||||||
<PathIcon Data="{StaticResource building_more}" Foreground="{DynamicResource ButtonDefaultTertiaryForeground}" />
|
<PathIcon Data="{StaticResource building_more}" Foreground="{DynamicResource ButtonDefaultTertiaryForeground}" />
|
||||||
|
@ -208,8 +209,8 @@
|
||||||
Grid.Row="9"
|
Grid.Row="9"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
Content="{x:Static resx:ResUI.TbSelectProfile}"
|
Click="BtnSelectPrevProfile_Click"
|
||||||
Click="BtnSelectPrevProfile_Click" />
|
Content="{x:Static resx:ResUI.TbSelectProfile}" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="10"
|
Grid.Row="10"
|
||||||
|
@ -228,8 +229,8 @@
|
||||||
Grid.Row="10"
|
Grid.Row="10"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
Content="{x:Static resx:ResUI.TbSelectProfile}"
|
Click="BtnSelectNextProfile_Click"
|
||||||
Click="BtnSelectNextProfile_Click" />
|
Content="{x:Static resx:ResUI.TbSelectProfile}" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="11"
|
Grid.Row="11"
|
||||||
|
|
|
@ -63,7 +63,7 @@ public partial class SubEditWindow : WindowBase<SubEditViewModel>
|
||||||
private async void BtnSelectPrevProfile_Click(object? sender, RoutedEventArgs e)
|
private async void BtnSelectPrevProfile_Click(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
var selectWindow = new ProfilesSelectWindow();
|
var selectWindow = new ProfilesSelectWindow();
|
||||||
selectWindow.SetConfigTypeFilter(new[] { EConfigType.Custom }, exclude: true);
|
selectWindow.SetConfigTypeFilter(new[] { EConfigType.Custom, EConfigType.PolicyGroup, EConfigType.ProxyChain }, exclude: true);
|
||||||
var result = await selectWindow.ShowDialog<bool?>(this);
|
var result = await selectWindow.ShowDialog<bool?>(this);
|
||||||
if (result == true)
|
if (result == true)
|
||||||
{
|
{
|
||||||
|
@ -78,7 +78,7 @@ public partial class SubEditWindow : WindowBase<SubEditViewModel>
|
||||||
private async void BtnSelectNextProfile_Click(object? sender, RoutedEventArgs e)
|
private async void BtnSelectNextProfile_Click(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
var selectWindow = new ProfilesSelectWindow();
|
var selectWindow = new ProfilesSelectWindow();
|
||||||
selectWindow.SetConfigTypeFilter(new[] { EConfigType.Custom }, exclude: true);
|
selectWindow.SetConfigTypeFilter(new[] { EConfigType.Custom, EConfigType.PolicyGroup, EConfigType.ProxyChain }, exclude: true);
|
||||||
var result = await selectWindow.ShowDialog<bool?>(this);
|
var result = await selectWindow.ShowDialog<bool?>(this);
|
||||||
if (result == true)
|
if (result == true)
|
||||||
{
|
{
|
||||||
|
|
213
v2rayN/v2rayN/Views/AddGroupServerWindow.xaml
Normal file
213
v2rayN/v2rayN/Views/AddGroupServerWindow.xaml
Normal file
|
@ -0,0 +1,213 @@
|
||||||
|
<base:WindowBase
|
||||||
|
x:Class="v2rayN.Views.AddGroupServerWindow"
|
||||||
|
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"
|
||||||
|
xmlns:reactiveui="http://reactiveui.net"
|
||||||
|
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||||
|
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||||
|
Title="{x:Static resx:ResUI.menuServers}"
|
||||||
|
Width="900"
|
||||||
|
Height="700"
|
||||||
|
x:TypeArguments="vms:AddGroupServerViewModel"
|
||||||
|
ResizeMode="CanResize"
|
||||||
|
ShowInTaskbar="False"
|
||||||
|
Style="{StaticResource WindowGlobal}"
|
||||||
|
WindowStartupLocation="CenterScreen"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
|
||||||
|
<DockPanel Margin="{StaticResource Margin8}">
|
||||||
|
<StackPanel
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
DockPanel.Dock="Bottom"
|
||||||
|
Orientation="Horizontal">
|
||||||
|
<Button
|
||||||
|
x:Name="btnSave"
|
||||||
|
Width="100"
|
||||||
|
Content="{x:Static resx:ResUI.TbConfirm}"
|
||||||
|
IsDefault="True"
|
||||||
|
Style="{StaticResource DefButton}" />
|
||||||
|
<Button
|
||||||
|
x:Name="btnCancel"
|
||||||
|
Width="100"
|
||||||
|
Margin="{StaticResource MarginLeftRight8}"
|
||||||
|
Content="{x:Static resx:ResUI.TbCancel}"
|
||||||
|
IsCancel="true"
|
||||||
|
Style="{StaticResource DefButton}" />
|
||||||
|
</StackPanel>
|
||||||
|
<Grid DockPanel.Dock="Top">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Grid Grid.Row="0">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="180" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
Style="{StaticResource ModuleTitle}"
|
||||||
|
Text="{x:Static resx:ResUI.menuServers}" />
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
|
Text="{x:Static resx:ResUI.TbRemarks}" />
|
||||||
|
<TextBox
|
||||||
|
x:Name="txtRemarks"
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="1"
|
||||||
|
Width="400"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
Style="{StaticResource DefTextBox}" />
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="2"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
|
Text="{x:Static resx:ResUI.TbCoreType}" />
|
||||||
|
<ComboBox
|
||||||
|
x:Name="cmbCoreType"
|
||||||
|
Grid.Row="2"
|
||||||
|
Grid.Column="1"
|
||||||
|
Width="200"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
materialDesign:HintAssist.Hint="{x:Static resx:ResUI.TbCoreType}"
|
||||||
|
Style="{StaticResource DefComboBox}" />
|
||||||
|
|
||||||
|
<Grid
|
||||||
|
x:Name="gridPolicyGroup"
|
||||||
|
Grid.Row="3"
|
||||||
|
Grid.Column="0"
|
||||||
|
Grid.ColumnSpan="3">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="180" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="3"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
|
Text="{x:Static resx:ResUI.TbPolicyGroupType}" />
|
||||||
|
<ComboBox
|
||||||
|
x:Name="cmbPolicyGroupType"
|
||||||
|
Grid.Row="3"
|
||||||
|
Grid.Column="1"
|
||||||
|
Width="200"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
materialDesign:HintAssist.Hint="{x:Static resx:ResUI.TbPolicyGroupType}"
|
||||||
|
Style="{StaticResource DefComboBox}" />
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<TabControl>
|
||||||
|
<TabItem HorizontalAlignment="Left" Header="{x:Static resx:ResUI.menuServerList}">
|
||||||
|
<DataGrid
|
||||||
|
x:Name="lstChild"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
BorderThickness="1"
|
||||||
|
CanUserAddRows="False"
|
||||||
|
CanUserResizeRows="False"
|
||||||
|
CanUserSortColumns="False"
|
||||||
|
EnableRowVirtualization="True"
|
||||||
|
GridLinesVisibility="All"
|
||||||
|
HeadersVisibility="Column"
|
||||||
|
IsReadOnly="True"
|
||||||
|
Style="{StaticResource DefDataGrid}">
|
||||||
|
<DataGrid.ContextMenu>
|
||||||
|
<ContextMenu Style="{StaticResource DefContextMenu}">
|
||||||
|
<MenuItem
|
||||||
|
x:Name="menuAddChildServer"
|
||||||
|
Height="{StaticResource MenuItemHeight}"
|
||||||
|
Click="MenuAddChild_Click"
|
||||||
|
Header="{x:Static resx:ResUI.menuAddChildServer}" />
|
||||||
|
<MenuItem
|
||||||
|
x:Name="menuRemoveChildServer"
|
||||||
|
Height="{StaticResource MenuItemHeight}"
|
||||||
|
Header="{x:Static resx:ResUI.menuRemoveChildServer}" />
|
||||||
|
<MenuItem
|
||||||
|
x:Name="menuSelectAllChild"
|
||||||
|
Height="{StaticResource MenuItemHeight}"
|
||||||
|
Header="{x:Static resx:ResUI.menuSelectAll}" />
|
||||||
|
<Separator />
|
||||||
|
<MenuItem
|
||||||
|
x:Name="menuMoveTop"
|
||||||
|
Height="{StaticResource MenuItemHeight}"
|
||||||
|
Header="{x:Static resx:ResUI.menuMoveTop}" />
|
||||||
|
<MenuItem
|
||||||
|
x:Name="menuMoveUp"
|
||||||
|
Height="{StaticResource MenuItemHeight}"
|
||||||
|
Header="{x:Static resx:ResUI.menuMoveUp}" />
|
||||||
|
<MenuItem
|
||||||
|
x:Name="menuMoveDown"
|
||||||
|
Height="{StaticResource MenuItemHeight}"
|
||||||
|
Header="{x:Static resx:ResUI.menuMoveDown}" />
|
||||||
|
<MenuItem
|
||||||
|
x:Name="menuMoveBottom"
|
||||||
|
Height="{StaticResource MenuItemHeight}"
|
||||||
|
Header="{x:Static resx:ResUI.menuMoveBottom}" />
|
||||||
|
</ContextMenu>
|
||||||
|
</DataGrid.ContextMenu>
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="150"
|
||||||
|
Binding="{Binding ConfigType}"
|
||||||
|
Header="{x:Static resx:ResUI.LvServiceType}" />
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="150"
|
||||||
|
Binding="{Binding Remarks}"
|
||||||
|
Header="{x:Static resx:ResUI.LvRemarks}" />
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="120"
|
||||||
|
Binding="{Binding Address}"
|
||||||
|
Header="{x:Static resx:ResUI.LvAddress}" />
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="100"
|
||||||
|
Binding="{Binding Port}"
|
||||||
|
Header="{x:Static resx:ResUI.LvPort}" />
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="100"
|
||||||
|
Binding="{Binding Network}"
|
||||||
|
Header="{x:Static resx:ResUI.LvTransportProtocol}" />
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="100"
|
||||||
|
Binding="{Binding StreamSecurity}"
|
||||||
|
Header="{x:Static resx:ResUI.LvTLS}" />
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
</TabItem>
|
||||||
|
</TabControl>
|
||||||
|
</DockPanel>
|
||||||
|
</base:WindowBase>
|
148
v2rayN/v2rayN/Views/AddGroupServerWindow.xaml.cs
Normal file
148
v2rayN/v2rayN/Views/AddGroupServerWindow.xaml.cs
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
using System.Reactive.Disposables;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using DynamicData;
|
||||||
|
using ReactiveUI;
|
||||||
|
|
||||||
|
namespace v2rayN.Views;
|
||||||
|
|
||||||
|
public partial class AddGroupServerWindow
|
||||||
|
{
|
||||||
|
public AddGroupServerWindow(ProfileItem profileItem)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
this.Owner = Application.Current.MainWindow;
|
||||||
|
this.Loaded += Window_Loaded;
|
||||||
|
this.PreviewKeyDown += AddGroupServerWindow_PreviewKeyDown;
|
||||||
|
lstChild.SelectionChanged += LstChild_SelectionChanged;
|
||||||
|
menuSelectAllChild.Click += MenuSelectAllChild_Click;
|
||||||
|
|
||||||
|
ViewModel = new AddGroupServerViewModel(profileItem, UpdateViewHandler);
|
||||||
|
|
||||||
|
cmbCoreType.ItemsSource = Global.CoreTypes;
|
||||||
|
cmbPolicyGroupType.ItemsSource = new List<string>
|
||||||
|
{
|
||||||
|
ResUI.TbLeastPing,
|
||||||
|
ResUI.TbFallback,
|
||||||
|
ResUI.TbRandom,
|
||||||
|
ResUI.TbRoundRobin,
|
||||||
|
ResUI.TbLeastLoad,
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (profileItem.ConfigType)
|
||||||
|
{
|
||||||
|
case EConfigType.PolicyGroup:
|
||||||
|
this.Title = ResUI.TbConfigTypePolicyGroup;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EConfigType.ProxyChain:
|
||||||
|
this.Title = ResUI.TbConfigTypeProxyChain;
|
||||||
|
gridPolicyGroup.Visibility = Visibility.Collapsed;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.WhenActivated(disposables =>
|
||||||
|
{
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Remarks, v => v.txtRemarks.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.CoreType, v => v.cmbCoreType.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.PolicyGroupType, v => v.cmbPolicyGroupType.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.OneWayBind(ViewModel, vm => vm.ChildItemsObs, v => v.lstChild.ItemsSource).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedChild, v => v.lstChild.SelectedItem).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.BindCommand(ViewModel, vm => vm.RemoveCmd, v => v.menuRemoveChildServer).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.MoveTopCmd, v => v.menuMoveTop).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.MoveUpCmd, v => v.menuMoveUp).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.MoveDownCmd, v => v.menuMoveDown).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.MoveBottomCmd, v => v.menuMoveBottom).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
||||||
|
});
|
||||||
|
WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
||||||
|
{
|
||||||
|
switch (action)
|
||||||
|
{
|
||||||
|
case EViewAction.CloseWindow:
|
||||||
|
this.DialogResult = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return await Task.FromResult(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Window_Loaded(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
txtRemarks.Focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddGroupServerWindow_PreviewKeyDown(object sender, KeyEventArgs e)
|
||||||
|
{
|
||||||
|
if (!lstChild.IsKeyboardFocusWithin)
|
||||||
|
return;
|
||||||
|
if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
|
||||||
|
{
|
||||||
|
if (e.Key == Key.A)
|
||||||
|
{
|
||||||
|
lstChild.SelectAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (e.Key == Key.T)
|
||||||
|
{
|
||||||
|
ViewModel?.MoveServer(EMove.Top);
|
||||||
|
}
|
||||||
|
else if (e.Key == Key.U)
|
||||||
|
{
|
||||||
|
ViewModel?.MoveServer(EMove.Up);
|
||||||
|
}
|
||||||
|
else if (e.Key == Key.D)
|
||||||
|
{
|
||||||
|
ViewModel?.MoveServer(EMove.Down);
|
||||||
|
}
|
||||||
|
else if (e.Key == Key.B)
|
||||||
|
{
|
||||||
|
ViewModel?.MoveServer(EMove.Bottom);
|
||||||
|
}
|
||||||
|
else if (e.Key == Key.Delete)
|
||||||
|
{
|
||||||
|
ViewModel?.ChildRemoveAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void MenuAddChild_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var selectWindow = new ProfilesSelectWindow();
|
||||||
|
if (ViewModel?.SelectedSource?.ConfigType == EConfigType.PolicyGroup)
|
||||||
|
{
|
||||||
|
selectWindow.SetConfigTypeFilter(new[] { EConfigType.Custom }, exclude: true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
selectWindow.SetConfigTypeFilter(new[] { EConfigType.Custom, EConfigType.PolicyGroup, EConfigType.ProxyChain }, exclude: true);
|
||||||
|
}
|
||||||
|
selectWindow.AllowMultiSelect(true);
|
||||||
|
if (selectWindow.ShowDialog() == true)
|
||||||
|
{
|
||||||
|
var profiles = await selectWindow.ProfileItems;
|
||||||
|
ViewModel?.ChildItemsObs.AddRange(profiles);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LstChild_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (ViewModel != null)
|
||||||
|
{
|
||||||
|
ViewModel.SelectedChildren = lstChild.SelectedItems.Cast<ProfileItem>().ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MenuSelectAllChild_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
lstChild.SelectAll();
|
||||||
|
}
|
||||||
|
}
|
|
@ -41,7 +41,7 @@
|
||||||
<TabControl HorizontalContentAlignment="Left">
|
<TabControl HorizontalContentAlignment="Left">
|
||||||
<TabItem Header="{x:Static resx:ResUI.ThBasicDNSSettings}">
|
<TabItem Header="{x:Static resx:ResUI.ThBasicDNSSettings}">
|
||||||
<ScrollViewer VerticalScrollBarVisibility="Visible">
|
<ScrollViewer VerticalScrollBarVisibility="Visible">
|
||||||
<Grid Margin="{StaticResource Margin8}">
|
<Grid x:Name="gridBasicDNSSettings" Margin="{StaticResource Margin8}">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
|
@ -100,65 +100,25 @@
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
IsEditable="True"
|
IsEditable="True"
|
||||||
Style="{StaticResource DefComboBox}" />
|
Style="{StaticResource DefComboBox}" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="3"
|
Grid.Row="2"
|
||||||
Grid.Column="0"
|
|
||||||
Margin="{StaticResource Margin8}"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Style="{StaticResource ToolbarTextBlock}"
|
|
||||||
Text="{x:Static resx:ResUI.TbSBOutboundsResolverDNS}" />
|
|
||||||
<ComboBox
|
|
||||||
x:Name="cmbSBResolverDNS"
|
|
||||||
Grid.Row="3"
|
|
||||||
Grid.Column="1"
|
|
||||||
Width="300"
|
|
||||||
Margin="{StaticResource Margin8}"
|
|
||||||
IsEditable="True"
|
|
||||||
Style="{StaticResource DefComboBox}" />
|
|
||||||
<TextBlock
|
|
||||||
Grid.Row="3"
|
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Style="{StaticResource ToolbarTextBlock}"
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
Text="{x:Static resx:ResUI.TbSBOutboundDomainResolve}"
|
Text="{x:Static resx:ResUI.TbRemoteDNSTips}"
|
||||||
TextWrapping="Wrap" />
|
TextWrapping="Wrap" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="4"
|
Grid.Row="3"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Style="{StaticResource ToolbarTextBlock}"
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
Text="{x:Static resx:ResUI.TbSBDoHResolverServer}" />
|
Text="{x:Static resx:ResUI.TbXrayFreedomStrategy}" />
|
||||||
<ComboBox
|
|
||||||
x:Name="cmbSBFinalResolverDNS"
|
|
||||||
Grid.Row="4"
|
|
||||||
Grid.Column="1"
|
|
||||||
Width="300"
|
|
||||||
Margin="{StaticResource Margin8}"
|
|
||||||
IsEditable="True"
|
|
||||||
Style="{StaticResource DefComboBox}" />
|
|
||||||
<TextBlock
|
|
||||||
Grid.Row="4"
|
|
||||||
Grid.Column="2"
|
|
||||||
Margin="{StaticResource Margin8}"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Style="{StaticResource ToolbarTextBlock}"
|
|
||||||
Text="{x:Static resx:ResUI.TbSBFallbackDNSResolve}"
|
|
||||||
TextWrapping="Wrap" />
|
|
||||||
|
|
||||||
<TextBlock
|
|
||||||
Grid.Row="5"
|
|
||||||
Grid.Column="0"
|
|
||||||
Margin="{StaticResource Margin8}"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Style="{StaticResource ToolbarTextBlock}"
|
|
||||||
Text="{x:Static resx:ResUI.TbXrayFreedomResolveStrategy}" />
|
|
||||||
<ComboBox
|
<ComboBox
|
||||||
x:Name="cmbRayFreedomDNSStrategy"
|
x:Name="cmbRayFreedomDNSStrategy"
|
||||||
Grid.Row="5"
|
Grid.Row="3"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="200"
|
Width="200"
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
|
@ -166,7 +126,7 @@
|
||||||
Style="{StaticResource DefComboBox}" />
|
Style="{StaticResource DefComboBox}" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="6"
|
Grid.Row="4"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
|
@ -174,7 +134,7 @@
|
||||||
Text="{x:Static resx:ResUI.TbSBDirectResolveStrategy}" />
|
Text="{x:Static resx:ResUI.TbSBDirectResolveStrategy}" />
|
||||||
<ComboBox
|
<ComboBox
|
||||||
x:Name="cmbSBDirectDNSStrategy"
|
x:Name="cmbSBDirectDNSStrategy"
|
||||||
Grid.Row="6"
|
Grid.Row="4"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="200"
|
Width="200"
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
|
@ -182,7 +142,7 @@
|
||||||
Style="{StaticResource DefComboBox}" />
|
Style="{StaticResource DefComboBox}" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="7"
|
Grid.Row="5"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
|
@ -190,7 +150,7 @@
|
||||||
Text="{x:Static resx:ResUI.TbSBRemoteResolveStrategy}" />
|
Text="{x:Static resx:ResUI.TbSBRemoteResolveStrategy}" />
|
||||||
<ComboBox
|
<ComboBox
|
||||||
x:Name="cmbSBRemoteDNSStrategy"
|
x:Name="cmbSBRemoteDNSStrategy"
|
||||||
Grid.Row="7"
|
Grid.Row="5"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="200"
|
Width="200"
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
|
@ -198,7 +158,7 @@
|
||||||
Style="{StaticResource DefComboBox}" />
|
Style="{StaticResource DefComboBox}" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="8"
|
Grid.Row="6"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
|
@ -206,23 +166,16 @@
|
||||||
Text="{x:Static resx:ResUI.TbAddCommonDNSHosts}" />
|
Text="{x:Static resx:ResUI.TbAddCommonDNSHosts}" />
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
x:Name="togAddCommonHosts"
|
x:Name="togAddCommonHosts"
|
||||||
Grid.Row="8"
|
Grid.Row="6"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
HorizontalAlignment="Left" />
|
HorizontalAlignment="Left" />
|
||||||
<TextBlock
|
|
||||||
Grid.Row="8"
|
|
||||||
Grid.Column="3"
|
|
||||||
Margin="{StaticResource Margin8}"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Style="{StaticResource ToolbarTextBlock}"
|
|
||||||
Text="{x:Static resx:ResUI.TbSBDoHOverride}" />
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem Header="{x:Static resx:ResUI.ThAdvancedDNSSettings}">
|
<TabItem Header="{x:Static resx:ResUI.ThAdvancedDNSSettings}">
|
||||||
<ScrollViewer VerticalScrollBarVisibility="Visible">
|
<ScrollViewer VerticalScrollBarVisibility="Visible">
|
||||||
<Grid Margin="{StaticResource Margin8}">
|
<Grid x:Name="gridAdvancedDNSSettings" Margin="{StaticResource Margin8}">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
|
@ -355,7 +308,7 @@
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
|
||||||
<TabItem Header="{x:Static resx:ResUI.TbSettingsCoreDns}">
|
<TabItem Header="{x:Static resx:ResUI.TbCustomDnsRay}">
|
||||||
<DockPanel Margin="{StaticResource Margin8}">
|
<DockPanel Margin="{StaticResource Margin8}">
|
||||||
<Grid DockPanel.Dock="Top">
|
<Grid DockPanel.Dock="Top">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
|
@ -452,7 +405,7 @@
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
|
||||||
<TabItem Header="{x:Static resx:ResUI.TbSettingsCoreDnsSingbox}">
|
<TabItem Header="{x:Static resx:ResUI.TbCustomDnsSingbox}">
|
||||||
<DockPanel Margin="{StaticResource Margin8}">
|
<DockPanel Margin="{StaticResource Margin8}">
|
||||||
<Grid DockPanel.Dock="Top">
|
<Grid DockPanel.Dock="Top">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Reactive.Disposables;
|
using System.Reactive.Disposables;
|
||||||
|
using System.Reactive.Linq;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
|
|
||||||
|
@ -21,9 +22,7 @@ public partial class DNSSettingWindow
|
||||||
cmbSBDirectDNSStrategy.ItemsSource = Global.SingboxDomainStrategy4Out;
|
cmbSBDirectDNSStrategy.ItemsSource = Global.SingboxDomainStrategy4Out;
|
||||||
cmbSBRemoteDNSStrategy.ItemsSource = Global.SingboxDomainStrategy4Out;
|
cmbSBRemoteDNSStrategy.ItemsSource = Global.SingboxDomainStrategy4Out;
|
||||||
cmbDirectDNS.ItemsSource = Global.DomainDirectDNSAddress;
|
cmbDirectDNS.ItemsSource = Global.DomainDirectDNSAddress;
|
||||||
cmbSBResolverDNS.ItemsSource = Global.DomainDirectDNSAddress.Concat(new[] { "dhcp://auto,localhost" });
|
|
||||||
cmbRemoteDNS.ItemsSource = Global.DomainRemoteDNSAddress;
|
cmbRemoteDNS.ItemsSource = Global.DomainRemoteDNSAddress;
|
||||||
cmbSBFinalResolverDNS.ItemsSource = Global.DomainPureIPDNSAddress.Concat(new[] { "dhcp://auto,localhost" });
|
|
||||||
cmbDirectExpectedIPs.ItemsSource = Global.ExpectedIPs;
|
cmbDirectExpectedIPs.ItemsSource = Global.ExpectedIPs;
|
||||||
|
|
||||||
cmbdomainStrategy4FreedomCompatible.ItemsSource = Global.DomainStrategy4Freedoms;
|
cmbdomainStrategy4FreedomCompatible.ItemsSource = Global.DomainStrategy4Freedoms;
|
||||||
|
@ -39,8 +38,6 @@ public partial class DNSSettingWindow
|
||||||
this.Bind(ViewModel, vm => vm.BlockBindingQuery, v => v.togBlockBindingQuery.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.DirectDNS, v => v.cmbDirectDNS.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.RemoteDNS, v => v.cmbRemoteDNS.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.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.RayStrategy4Freedom, v => v.cmbRayFreedomDNSStrategy.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SingboxStrategy4Direct, v => v.cmbSBDirectDNSStrategy.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SingboxStrategy4Direct, v => v.cmbSBDirectDNSStrategy.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SingboxStrategy4Proxy, v => v.cmbSBRemoteDNSStrategy.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SingboxStrategy4Proxy, v => v.cmbSBRemoteDNSStrategy.Text).DisposeWith(disposables);
|
||||||
|
@ -65,18 +62,16 @@ public partial class DNSSettingWindow
|
||||||
this.BindCommand(ViewModel, vm => vm.ImportDefConfig4V2rayCompatibleCmd, v => v.btnImportDefConfig4V2rayCompatible).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.BindCommand(ViewModel, vm => vm.ImportDefConfig4SingboxCompatibleCmd, v => v.btnImportDefConfig4SingboxCompatible).DisposeWith(disposables);
|
||||||
|
|
||||||
this.WhenAnyValue(
|
this.WhenAnyValue(x => x.ViewModel.IsSimpleDNSEnabled)
|
||||||
x => x.ViewModel.RayCustomDNSEnableCompatible,
|
.Select(b => b ? Visibility.Collapsed : Visibility.Visible)
|
||||||
x => x.ViewModel.SBCustomDNSEnableCompatible,
|
|
||||||
(ray, sb) => ray && sb ? Visibility.Visible : Visibility.Collapsed)
|
|
||||||
.BindTo(this, x => x.txtBasicDNSSettingsInvalid.Visibility)
|
.BindTo(this, x => x.txtBasicDNSSettingsInvalid.Visibility)
|
||||||
.DisposeWith(disposables);
|
.DisposeWith(disposables);
|
||||||
this.WhenAnyValue(
|
this.WhenAnyValue(x => x.ViewModel.IsSimpleDNSEnabled)
|
||||||
x => x.ViewModel.RayCustomDNSEnableCompatible,
|
.Select(b => b ? Visibility.Collapsed : Visibility.Visible)
|
||||||
x => x.ViewModel.SBCustomDNSEnableCompatible,
|
|
||||||
(ray, sb) => ray && sb ? Visibility.Visible : Visibility.Collapsed)
|
|
||||||
.BindTo(this, x => x.txtAdvancedDNSSettingsInvalid.Visibility)
|
.BindTo(this, x => x.txtAdvancedDNSSettingsInvalid.Visibility)
|
||||||
.DisposeWith(disposables);
|
.DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.IsSimpleDNSEnabled, v => v.gridBasicDNSSettings.IsEnabled).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.IsSimpleDNSEnabled, v => v.gridAdvancedDNSSettings.IsEnabled).DisposeWith(disposables);
|
||||||
});
|
});
|
||||||
WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme);
|
WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme);
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,6 +71,14 @@
|
||||||
x:Name="menuAddCustomServer"
|
x:Name="menuAddCustomServer"
|
||||||
Height="{StaticResource MenuItemHeight}"
|
Height="{StaticResource MenuItemHeight}"
|
||||||
Header="{x:Static resx:ResUI.menuAddCustomServer}" />
|
Header="{x:Static resx:ResUI.menuAddCustomServer}" />
|
||||||
|
<MenuItem
|
||||||
|
x:Name="menuAddPolicyGroupServer"
|
||||||
|
Height="{StaticResource MenuItemHeight}"
|
||||||
|
Header="{x:Static resx:ResUI.menuAddPolicyGroupServer}" />
|
||||||
|
<MenuItem
|
||||||
|
x:Name="menuAddProxyChainServer"
|
||||||
|
Height="{StaticResource MenuItemHeight}"
|
||||||
|
Header="{x:Static resx:ResUI.menuAddProxyChainServer}" />
|
||||||
<Separator Margin="-40,5" />
|
<Separator Margin="-40,5" />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
x:Name="menuAddVmessServer"
|
x:Name="menuAddVmessServer"
|
||||||
|
|
|
@ -79,6 +79,8 @@ public partial class MainWindow
|
||||||
this.BindCommand(ViewModel, vm => vm.AddWireguardServerCmd, v => v.menuAddWireguardServer).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.AddAnytlsServerCmd, v => v.menuAddAnytlsServer).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.AddCustomServerCmd, v => v.menuAddCustomServer).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.AddCustomServerCmd, v => v.menuAddCustomServer).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.AddPolicyGroupServerCmd, v => v.menuAddPolicyGroupServer).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.AddProxyChainServerCmd, v => v.menuAddProxyChainServer).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.AddServerViaClipboardCmd, v => v.menuAddServerViaClipboard).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.AddServerViaClipboardCmd, v => v.menuAddServerViaClipboard).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.AddServerViaScanCmd, v => v.menuAddServerViaScan).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.AddServerViaScanCmd, v => v.menuAddServerViaScan).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.AddServerViaImageCmd, v => v.menuAddServerViaImage).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.AddServerViaImageCmd, v => v.menuAddServerViaImage).DisposeWith(disposables);
|
||||||
|
@ -156,6 +158,10 @@ public partial class MainWindow
|
||||||
});
|
});
|
||||||
|
|
||||||
this.Title = $"{Utils.GetVersion()} - {(Utils.IsAdministrator() ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}";
|
this.Title = $"{Utils.GetVersion()} - {(Utils.IsAdministrator() ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}";
|
||||||
|
if (_config.UiItem.AutoHideStartup)
|
||||||
|
{
|
||||||
|
this.WindowState = WindowState.Minimized;
|
||||||
|
}
|
||||||
|
|
||||||
if (!_config.GuiItem.EnableHWA)
|
if (!_config.GuiItem.EnableHWA)
|
||||||
{
|
{
|
||||||
|
@ -195,6 +201,11 @@ public partial class MainWindow
|
||||||
return false;
|
return false;
|
||||||
return (new AddServer2Window((ProfileItem)obj)).ShowDialog() ?? false;
|
return (new AddServer2Window((ProfileItem)obj)).ShowDialog() ?? false;
|
||||||
|
|
||||||
|
case EViewAction.AddGroupServerWindow:
|
||||||
|
if (obj is null)
|
||||||
|
return false;
|
||||||
|
return (new AddGroupServerWindow((ProfileItem)obj)).ShowDialog() ?? false;
|
||||||
|
|
||||||
case EViewAction.DNSSettingWindow:
|
case EViewAction.DNSSettingWindow:
|
||||||
return (new DNSSettingWindow().ShowDialog() ?? false);
|
return (new DNSSettingWindow().ShowDialog() ?? false);
|
||||||
|
|
||||||
|
@ -383,6 +394,10 @@ public partial class MainWindow
|
||||||
protected override void OnLoaded(object? sender, RoutedEventArgs e)
|
protected override void OnLoaded(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnLoaded(sender, e);
|
base.OnLoaded(sender, e);
|
||||||
|
if (_config.UiItem.AutoHideStartup)
|
||||||
|
{
|
||||||
|
ShowHideWindow(false);
|
||||||
|
}
|
||||||
RestoreUI();
|
RestoreUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -125,28 +125,32 @@
|
||||||
Height="{StaticResource MenuItemHeight}"
|
Height="{StaticResource MenuItemHeight}"
|
||||||
Header="{x:Static resx:ResUI.menuShareServer}" />
|
Header="{x:Static resx:ResUI.menuShareServer}" />
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuItem Header="{x:Static resx:ResUI.menuSetDefaultMultipleServer}">
|
<MenuItem Header="{x:Static resx:ResUI.menuGenGroupMultipleServer}">
|
||||||
<MenuItem
|
<MenuItem
|
||||||
x:Name="menuSetDefaultMultipleServerXrayRandom"
|
x:Name="menuGenGroupMultipleServerXrayRandom"
|
||||||
Height="{StaticResource MenuItemHeight}"
|
Height="{StaticResource MenuItemHeight}"
|
||||||
Header="{x:Static resx:ResUI.menuSetDefaultMultipleServerXrayRandom}" />
|
Header="{x:Static resx:ResUI.menuGenGroupMultipleServerXrayRandom}" />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
x:Name="menuSetDefaultMultipleServerXrayRoundRobin"
|
x:Name="menuGenGroupMultipleServerXrayRoundRobin"
|
||||||
Height="{StaticResource MenuItemHeight}"
|
Height="{StaticResource MenuItemHeight}"
|
||||||
Header="{x:Static resx:ResUI.menuSetDefaultMultipleServerXrayRoundRobin}" />
|
Header="{x:Static resx:ResUI.menuGenGroupMultipleServerXrayRoundRobin}" />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
x:Name="menuSetDefaultMultipleServerXrayLeastPing"
|
x:Name="menuGenGroupMultipleServerXrayLeastPing"
|
||||||
Height="{StaticResource MenuItemHeight}"
|
Height="{StaticResource MenuItemHeight}"
|
||||||
Header="{x:Static resx:ResUI.menuSetDefaultMultipleServerXrayLeastPing}" />
|
Header="{x:Static resx:ResUI.menuGenGroupMultipleServerXrayLeastPing}" />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
x:Name="menuSetDefaultMultipleServerXrayLeastLoad"
|
x:Name="menuGenGroupMultipleServerXrayLeastLoad"
|
||||||
Height="{StaticResource MenuItemHeight}"
|
Height="{StaticResource MenuItemHeight}"
|
||||||
Header="{x:Static resx:ResUI.menuSetDefaultMultipleServerXrayLeastLoad}" />
|
Header="{x:Static resx:ResUI.menuGenGroupMultipleServerXrayLeastLoad}" />
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
x:Name="menuSetDefaultMultipleServerSingBoxLeastPing"
|
x:Name="menuGenGroupMultipleServerSingBoxLeastPing"
|
||||||
Height="{StaticResource MenuItemHeight}"
|
Height="{StaticResource MenuItemHeight}"
|
||||||
Header="{x:Static resx:ResUI.menuSetDefaultMultipleServerSingBoxLeastPing}" />
|
Header="{x:Static resx:ResUI.menuGenGroupMultipleServerSingBoxLeastPing}" />
|
||||||
|
<MenuItem
|
||||||
|
x:Name="menuGenGroupMultipleServerSingBoxFallback"
|
||||||
|
Height="{StaticResource MenuItemHeight}"
|
||||||
|
Header="{x:Static resx:ResUI.menuGenGroupMultipleServerSingBoxFallback}" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
|
|
|
@ -60,11 +60,12 @@ public partial class ProfilesView
|
||||||
this.BindCommand(ViewModel, vm => vm.CopyServerCmd, v => v.menuCopyServer).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.CopyServerCmd, v => v.menuCopyServer).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.SetDefaultServerCmd, v => v.menuSetDefaultServer).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.SetDefaultServerCmd, v => v.menuSetDefaultServer).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.ShareServerCmd, v => v.menuShareServer).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.ShareServerCmd, v => v.menuShareServer).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.SetDefaultMultipleServerXrayRandomCmd, v => v.menuSetDefaultMultipleServerXrayRandom).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerXrayRandomCmd, v => v.menuGenGroupMultipleServerXrayRandom).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.SetDefaultMultipleServerXrayRoundRobinCmd, v => v.menuSetDefaultMultipleServerXrayRoundRobin).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerXrayRoundRobinCmd, v => v.menuGenGroupMultipleServerXrayRoundRobin).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.SetDefaultMultipleServerXrayLeastPingCmd, v => v.menuSetDefaultMultipleServerXrayLeastPing).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerXrayLeastPingCmd, v => v.menuGenGroupMultipleServerXrayLeastPing).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.SetDefaultMultipleServerXrayLeastLoadCmd, v => v.menuSetDefaultMultipleServerXrayLeastLoad).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerXrayLeastLoadCmd, v => v.menuGenGroupMultipleServerXrayLeastLoad).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.SetDefaultMultipleServerSingBoxLeastPingCmd, v => v.menuSetDefaultMultipleServerSingBoxLeastPing).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerSingBoxLeastPingCmd, v => v.menuGenGroupMultipleServerSingBoxLeastPing).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerSingBoxFallbackCmd, v => v.menuGenGroupMultipleServerSingBoxFallback).DisposeWith(disposables);
|
||||||
|
|
||||||
//servers move
|
//servers move
|
||||||
this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.cmbMoveToGroup.ItemsSource).DisposeWith(disposables);
|
this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.cmbMoveToGroup.ItemsSource).DisposeWith(disposables);
|
||||||
|
@ -148,6 +149,11 @@ public partial class ProfilesView
|
||||||
return false;
|
return false;
|
||||||
return (new AddServer2Window((ProfileItem)obj)).ShowDialog() ?? false;
|
return (new AddServer2Window((ProfileItem)obj)).ShowDialog() ?? false;
|
||||||
|
|
||||||
|
case EViewAction.AddGroupServerWindow:
|
||||||
|
if (obj is null)
|
||||||
|
return false;
|
||||||
|
return (new AddGroupServerWindow((ProfileItem)obj)).ShowDialog() ?? false;
|
||||||
|
|
||||||
case EViewAction.ShareServer:
|
case EViewAction.ShareServer:
|
||||||
if (obj is null)
|
if (obj is null)
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
|
@ -48,13 +49,19 @@
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
Style="{StaticResource DefTextBox}" />
|
Style="{StaticResource DefTextBox}" />
|
||||||
<ToggleButton
|
<StackPanel
|
||||||
x:Name="togEnabled"
|
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
VerticalAlignment="Center" />
|
VerticalAlignment="Center"
|
||||||
|
Orientation="Horizontal">
|
||||||
|
<ToggleButton
|
||||||
|
x:Name="togEnabled"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
|
@ -62,10 +69,34 @@
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Style="{StaticResource ToolbarTextBlock}"
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
|
Text="{x:Static resx:ResUI.TbRuleType}" />
|
||||||
|
<ComboBox
|
||||||
|
x:Name="cmbRuleType"
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="1"
|
||||||
|
Width="200"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
MaxDropDownHeight="1000"
|
||||||
|
Style="{StaticResource DefComboBox}" />
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="2"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
|
Text="{x:Static resx:ResUI.TbRuleTypeTips}" />
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="2"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
Text="outboundTag" />
|
Text="outboundTag" />
|
||||||
<ComboBox
|
<ComboBox
|
||||||
x:Name="cmbOutboundTag"
|
x:Name="cmbOutboundTag"
|
||||||
Grid.Row="1"
|
Grid.Row="2"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="200"
|
Width="200"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
|
@ -73,7 +104,7 @@
|
||||||
MaxDropDownHeight="1000"
|
MaxDropDownHeight="1000"
|
||||||
Style="{StaticResource DefComboBox}" />
|
Style="{StaticResource DefComboBox}" />
|
||||||
<StackPanel
|
<StackPanel
|
||||||
Grid.Row="1"
|
Grid.Row="2"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
|
@ -92,7 +123,7 @@
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="2"
|
Grid.Row="3"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
|
@ -100,14 +131,14 @@
|
||||||
Text="port" />
|
Text="port" />
|
||||||
<TextBox
|
<TextBox
|
||||||
x:Name="txtPort"
|
x:Name="txtPort"
|
||||||
Grid.Row="2"
|
Grid.Row="3"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="200"
|
Width="200"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
Style="{StaticResource DefTextBox}" />
|
Style="{StaticResource DefTextBox}" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="2"
|
Grid.Row="3"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
|
@ -116,7 +147,7 @@
|
||||||
Text="{x:Static resx:ResUI.TbRuleMatchingTips}" />
|
Text="{x:Static resx:ResUI.TbRuleMatchingTips}" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="3"
|
Grid.Row="4"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
|
@ -124,14 +155,14 @@
|
||||||
Text="protocol" />
|
Text="protocol" />
|
||||||
<ListBox
|
<ListBox
|
||||||
x:Name="clbProtocol"
|
x:Name="clbProtocol"
|
||||||
Grid.Row="3"
|
Grid.Row="4"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
FontSize="{DynamicResource StdFontSize}"
|
FontSize="{DynamicResource StdFontSize}"
|
||||||
Style="{StaticResource MaterialDesignFilterChipPrimaryListBox}" />
|
Style="{StaticResource MaterialDesignFilterChipPrimaryListBox}" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="3"
|
Grid.Row="4"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
|
@ -143,7 +174,7 @@
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="4"
|
Grid.Row="5"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
|
@ -151,13 +182,13 @@
|
||||||
Text="inboundTag" />
|
Text="inboundTag" />
|
||||||
<ListBox
|
<ListBox
|
||||||
x:Name="clbInboundTag"
|
x:Name="clbInboundTag"
|
||||||
Grid.Row="4"
|
Grid.Row="5"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
FontSize="{DynamicResource StdFontSize}"
|
FontSize="{DynamicResource StdFontSize}"
|
||||||
Style="{StaticResource MaterialDesignFilterChipPrimaryListBox}" />
|
Style="{StaticResource MaterialDesignFilterChipPrimaryListBox}" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="4"
|
Grid.Row="5"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
|
@ -166,7 +197,7 @@
|
||||||
Text="{x:Static resx:ResUI.TbRoutingInboundTagTips}" />
|
Text="{x:Static resx:ResUI.TbRoutingInboundTagTips}" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="5"
|
Grid.Row="6"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
|
@ -174,7 +205,7 @@
|
||||||
Text="network" />
|
Text="network" />
|
||||||
<ComboBox
|
<ComboBox
|
||||||
x:Name="cmbNetwork"
|
x:Name="cmbNetwork"
|
||||||
Grid.Row="5"
|
Grid.Row="6"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="200"
|
Width="200"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
|
@ -182,7 +213,7 @@
|
||||||
Style="{StaticResource DefComboBox}" />
|
Style="{StaticResource DefComboBox}" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="5"
|
Grid.Row="6"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
|
|
|
@ -21,6 +21,7 @@ public partial class RoutingRuleDetailsWindow
|
||||||
clbProtocol.ItemsSource = Global.RuleProtocols;
|
clbProtocol.ItemsSource = Global.RuleProtocols;
|
||||||
clbInboundTag.ItemsSource = Global.InboundTags;
|
clbInboundTag.ItemsSource = Global.InboundTags;
|
||||||
cmbNetwork.ItemsSource = Global.RuleNetworks;
|
cmbNetwork.ItemsSource = Global.RuleNetworks;
|
||||||
|
cmbRuleType.ItemsSource = Utils.GetEnumNames<ERuleType>().AppendEmpty();
|
||||||
|
|
||||||
if (!rulesItem.Id.IsNullOrEmpty())
|
if (!rulesItem.Id.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
|
@ -45,6 +46,7 @@ public partial class RoutingRuleDetailsWindow
|
||||||
this.Bind(ViewModel, vm => vm.IP, v => v.txtIP.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.IP, v => v.txtIP.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.Process, v => v.txtProcess.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.Process, v => v.txtProcess.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.AutoSort, v => v.chkAutoSort.IsChecked).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.AutoSort, v => v.chkAutoSort.IsChecked).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.RuleType, v => v.cmbRuleType.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
||||||
});
|
});
|
||||||
|
|
|
@ -301,7 +301,11 @@
|
||||||
Binding="{Binding Remarks}"
|
Binding="{Binding Remarks}"
|
||||||
Header="{x:Static resx:ResUI.LvRemarks}" />
|
Header="{x:Static resx:ResUI.LvRemarks}" />
|
||||||
<DataGridTextColumn
|
<DataGridTextColumn
|
||||||
Width="120"
|
Width="100"
|
||||||
|
Binding="{Binding RuleTypeName}"
|
||||||
|
Header="{x:Static resx:ResUI.TbRuleType}" />
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="130"
|
||||||
Binding="{Binding OutboundTag}"
|
Binding="{Binding OutboundTag}"
|
||||||
Header="outboundTag" />
|
Header="outboundTag" />
|
||||||
<DataGridTextColumn
|
<DataGridTextColumn
|
||||||
|
@ -317,17 +321,13 @@
|
||||||
Binding="{Binding InboundTags}"
|
Binding="{Binding InboundTags}"
|
||||||
Header="inboundTag" />
|
Header="inboundTag" />
|
||||||
<DataGridTextColumn
|
<DataGridTextColumn
|
||||||
Width="90"
|
Width="100"
|
||||||
Binding="{Binding Network}"
|
Binding="{Binding Network}"
|
||||||
Header="network" />
|
Header="network" />
|
||||||
<DataGridTextColumn
|
<DataGridTextColumn
|
||||||
Width="*"
|
Width="*"
|
||||||
Binding="{Binding Domains}"
|
Binding="{Binding Domains}"
|
||||||
Header="domain" />
|
Header="domain / ip" />
|
||||||
<DataGridTextColumn
|
|
||||||
Width="*"
|
|
||||||
Binding="{Binding Ips}"
|
|
||||||
Header="ip" />
|
|
||||||
</DataGrid.Columns>
|
</DataGrid.Columns>
|
||||||
</DataGrid>
|
</DataGrid>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
|
|
@ -117,7 +117,7 @@
|
||||||
<materialDesign:PopupBox
|
<materialDesign:PopupBox
|
||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Left"
|
||||||
StaysOpen="True"
|
StaysOpen="True"
|
||||||
Style="{StaticResource MaterialDesignToolForegroundPopupBox}">
|
Style="{StaticResource MaterialDesignToolForegroundPopupBox}">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
|
|
|
@ -57,7 +57,7 @@ public partial class SubEditWindow
|
||||||
private async void BtnSelectPrevProfile_Click(object sender, RoutedEventArgs e)
|
private async void BtnSelectPrevProfile_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
var selectWindow = new ProfilesSelectWindow();
|
var selectWindow = new ProfilesSelectWindow();
|
||||||
selectWindow.SetConfigTypeFilter(new[] { EConfigType.Custom }, exclude: true);
|
selectWindow.SetConfigTypeFilter(new[] { EConfigType.Custom, EConfigType.PolicyGroup, EConfigType.ProxyChain }, exclude: true);
|
||||||
if (selectWindow.ShowDialog() == true)
|
if (selectWindow.ShowDialog() == true)
|
||||||
{
|
{
|
||||||
var profile = await selectWindow.ProfileItem;
|
var profile = await selectWindow.ProfileItem;
|
||||||
|
@ -71,7 +71,7 @@ public partial class SubEditWindow
|
||||||
private async void BtnSelectNextProfile_Click(object sender, RoutedEventArgs e)
|
private async void BtnSelectNextProfile_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
var selectWindow = new ProfilesSelectWindow();
|
var selectWindow = new ProfilesSelectWindow();
|
||||||
selectWindow.SetConfigTypeFilter(new[] { EConfigType.Custom }, exclude: true);
|
selectWindow.SetConfigTypeFilter(new[] { EConfigType.Custom, EConfigType.PolicyGroup, EConfigType.ProxyChain }, exclude: true);
|
||||||
if (selectWindow.ShowDialog() == true)
|
if (selectWindow.ShowDialog() == true)
|
||||||
{
|
{
|
||||||
var profile = await selectWindow.ProfileItem;
|
var profile = await selectWindow.ProfileItem;
|
||||||
|
|
Loading…
Reference in a new issue