mirror of
https://github.com/2dust/v2rayN.git
synced 2025-10-29 11:32:53 +00:00
Merge a898c57518 into 7995bdd4df
This commit is contained in:
commit
bff32b017f
51 changed files with 3202 additions and 453 deletions
|
|
@ -813,7 +813,7 @@ public class Utils
|
|||
}
|
||||
}
|
||||
|
||||
public static string GetBinConfigPath(string filename = "")
|
||||
public static string GetBinConfigPath(string filename = "", ECoreType coreType = ECoreType.v2rayN)
|
||||
{
|
||||
var tempPath = Path.Combine(StartupPath(), "binConfigs");
|
||||
if (!Directory.Exists(tempPath))
|
||||
|
|
@ -827,10 +827,27 @@ public class Utils
|
|||
}
|
||||
else
|
||||
{
|
||||
return Path.Combine(tempPath, filename);
|
||||
return Path.Combine(tempPath, GetBinConfigFileName(filename, coreType));
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetBinConfigFileName(string filename, ECoreType coreType = ECoreType.v2rayN)
|
||||
{
|
||||
var fileSuffix = coreType switch
|
||||
{
|
||||
ECoreType.sing_box => ".json",
|
||||
ECoreType.Xray => ".json",
|
||||
ECoreType.hysteria2 => ".json",
|
||||
ECoreType.naiveproxy => ".json",
|
||||
ECoreType.tuic => ".json",
|
||||
ECoreType.juicity => ".json",
|
||||
ECoreType.brook => ".cac",
|
||||
ECoreType.shadowquic => ".yaml",
|
||||
_ => string.Empty
|
||||
};
|
||||
return filename.EndsWith(fileSuffix) ? filename : $"{filename}{fileSuffix}";
|
||||
}
|
||||
|
||||
#endregion TempPath
|
||||
|
||||
#region Platform
|
||||
|
|
|
|||
|
|
@ -12,5 +12,9 @@ public enum EConfigType
|
|||
TUIC = 8,
|
||||
WireGuard = 9,
|
||||
HTTP = 10,
|
||||
Anytls = 11
|
||||
Anytls = 11,
|
||||
NaiveProxy = 100,
|
||||
Juicity = 101,
|
||||
Brook = 102,
|
||||
Shadowquic = 103,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,5 +9,6 @@ public enum EInboundProtocol
|
|||
api,
|
||||
api2,
|
||||
mixed,
|
||||
split,
|
||||
speedtest = 21
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ public class Global
|
|||
|
||||
public const string PromotionUrl = @"aHR0cHM6Ly85LjIzNDQ1Ni54eXovYWJjLmh0bWw=";
|
||||
public const string ConfigFileName = "guiNConfig.json";
|
||||
public const string CoreConfigFileName = "config.json";
|
||||
public const string CorePreConfigFileName = "configPre.json";
|
||||
public const string CoreConfigFileName = "config";
|
||||
public const string CorePreConfigFileName = "configPre";
|
||||
public const string CoreSpeedtestConfigFileName = "configTest{0}.json";
|
||||
public const string CoreMultipleLoadConfigFileName = "configMultipleLoad.json";
|
||||
public const string ClashMixinConfigFileName = "Mixin.yaml";
|
||||
|
|
@ -170,7 +170,11 @@ public class Global
|
|||
{ EConfigType.Hysteria2, "hysteria2://" },
|
||||
{ EConfigType.TUIC, "tuic://" },
|
||||
{ EConfigType.WireGuard, "wireguard://" },
|
||||
{ EConfigType.Anytls, "anytls://" }
|
||||
{ EConfigType.Anytls, "anytls://" },
|
||||
{ EConfigType.NaiveProxy, "naive://" },
|
||||
{ EConfigType.Juicity, "juicity://" },
|
||||
{ EConfigType.Brook, "brook://" },
|
||||
{ EConfigType.Shadowquic, "shadowquic://" }
|
||||
};
|
||||
|
||||
public static readonly Dictionary<EConfigType, string> ProtocolTypes = new()
|
||||
|
|
@ -184,7 +188,11 @@ public class Global
|
|||
{ EConfigType.Hysteria2, "hysteria2" },
|
||||
{ EConfigType.TUIC, "tuic" },
|
||||
{ EConfigType.WireGuard, "wireguard" },
|
||||
{ EConfigType.Anytls, "anytls" }
|
||||
{ EConfigType.Anytls, "anytls" },
|
||||
{ EConfigType.NaiveProxy, "naiveproxy" },
|
||||
{ EConfigType.Juicity, "juicity" },
|
||||
{ EConfigType.Brook, "brook" },
|
||||
{ EConfigType.Shadowquic, "shadowquic" }
|
||||
};
|
||||
|
||||
public static readonly List<string> VmessSecurities =
|
||||
|
|
@ -278,6 +286,75 @@ public class Global
|
|||
"sing_box"
|
||||
];
|
||||
|
||||
public static readonly List<string> Hysteria2CoreTypes =
|
||||
[
|
||||
"sing_box",
|
||||
"hysteria2"
|
||||
];
|
||||
|
||||
public static readonly List<string> TuicCoreTypes =
|
||||
[
|
||||
"sing_box",
|
||||
"tuic"
|
||||
];
|
||||
|
||||
public static readonly List<string> NaiveProxyCoreTypes =
|
||||
[
|
||||
"naiveproxy"
|
||||
];
|
||||
|
||||
public static readonly List<string> JuicityProxyCoreTypes =
|
||||
[
|
||||
"juicity"
|
||||
];
|
||||
|
||||
public static readonly List<string> BrookCoreTypes =
|
||||
[
|
||||
"brook"
|
||||
];
|
||||
|
||||
public static readonly List<string> ShadowquicCoreTypes =
|
||||
[
|
||||
"shadowquic"
|
||||
];
|
||||
|
||||
public static readonly List<EConfigType> SupportSplitConfigTypes =
|
||||
[
|
||||
EConfigType.VMess,
|
||||
EConfigType.VLESS,
|
||||
EConfigType.Shadowsocks,
|
||||
EConfigType.Trojan,
|
||||
EConfigType.Hysteria2,
|
||||
EConfigType.TUIC,
|
||||
EConfigType.WireGuard,
|
||||
EConfigType.SOCKS,
|
||||
];
|
||||
|
||||
public static readonly HashSet<EConfigType> XraySupportConfigType =
|
||||
[
|
||||
EConfigType.VMess,
|
||||
EConfigType.VLESS,
|
||||
EConfigType.Shadowsocks,
|
||||
EConfigType.Trojan,
|
||||
EConfigType.WireGuard,
|
||||
EConfigType.SOCKS,
|
||||
EConfigType.HTTP,
|
||||
];
|
||||
|
||||
public static readonly HashSet<EConfigType> SingboxSupportConfigType =
|
||||
[
|
||||
EConfigType.VMess,
|
||||
EConfigType.VLESS,
|
||||
EConfigType.Shadowsocks,
|
||||
EConfigType.Trojan,
|
||||
EConfigType.Hysteria2,
|
||||
EConfigType.TUIC,
|
||||
EConfigType.Anytls,
|
||||
EConfigType.WireGuard,
|
||||
EConfigType.SOCKS,
|
||||
EConfigType.HTTP,
|
||||
];
|
||||
|
||||
public static readonly List<string> DomainStrategies =
|
||||
[
|
||||
"AsIs",
|
||||
|
|
@ -464,13 +541,20 @@ public class Global
|
|||
""
|
||||
];
|
||||
|
||||
public static readonly List<string> TuicCongestionControls =
|
||||
public static readonly List<string> CongestionControls =
|
||||
[
|
||||
"cubic",
|
||||
"new_reno",
|
||||
"bbr"
|
||||
];
|
||||
|
||||
public static readonly List<string> NaiveProxyProtocols =
|
||||
[
|
||||
"https",
|
||||
"http",
|
||||
"quic"
|
||||
];
|
||||
|
||||
public static readonly List<string> allowSelectType =
|
||||
[
|
||||
"selector",
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
using DynamicData;
|
||||
using ServiceLib.Enums;
|
||||
using ServiceLib.Models;
|
||||
|
||||
namespace ServiceLib.Handler;
|
||||
|
||||
public sealed class AppHandler
|
||||
|
|
@ -231,9 +235,89 @@ public sealed class AppHandler
|
|||
return (ECoreType)profileItem.CoreType;
|
||||
}
|
||||
|
||||
return GetCoreType(eConfigType);
|
||||
}
|
||||
|
||||
public ECoreType GetCoreType(EConfigType eConfigType)
|
||||
{
|
||||
var item = _config.CoreTypeItem?.FirstOrDefault(it => it.ConfigType == eConfigType);
|
||||
return item?.CoreType ?? ECoreType.Xray;
|
||||
}
|
||||
|
||||
public ECoreType GetSplitCoreType(ProfileItem profileItem, EConfigType eConfigType)
|
||||
{
|
||||
if (profileItem?.CoreType != null)
|
||||
{
|
||||
return (ECoreType)profileItem.CoreType;
|
||||
}
|
||||
|
||||
return GetSplitCoreType(eConfigType);
|
||||
}
|
||||
|
||||
public ECoreType GetSplitCoreType(EConfigType eConfigType)
|
||||
{
|
||||
var item = _config.SplitCoreItem.SplitCoreTypes?.FirstOrDefault(it => it.ConfigType == eConfigType);
|
||||
return item?.CoreType ?? ECoreType.Xray;
|
||||
}
|
||||
|
||||
public (bool, ECoreType, ECoreType?) GetCoreAndPreType(ProfileItem profileItem)
|
||||
{
|
||||
var splitCore = _config.SplitCoreItem.EnableSplitCore;
|
||||
var coreType = GetCoreType(profileItem, profileItem.ConfigType);
|
||||
ECoreType? preCoreType = null;
|
||||
|
||||
var pureEndpointCore = profileItem.CoreType ?? GetSplitCoreType(profileItem, profileItem.ConfigType);
|
||||
var splitRouteCore = _config.SplitCoreItem.RouteCoreType;
|
||||
var enableTun = _config.TunModeItem.EnableTun;
|
||||
|
||||
if (profileItem.ConfigType == EConfigType.Custom)
|
||||
{
|
||||
splitCore = false;
|
||||
coreType = profileItem.CoreType ?? ECoreType.Xray;
|
||||
if (profileItem.PreSocksPort > 0)
|
||||
{
|
||||
preCoreType = enableTun ? ECoreType.sing_box : GetCoreType(profileItem.ConfigType);
|
||||
}
|
||||
else
|
||||
{
|
||||
preCoreType = null;
|
||||
}
|
||||
}
|
||||
else if (!splitCore && profileItem.CoreType is not (ECoreType.Xray or ECoreType.sing_box))
|
||||
{
|
||||
// Force SplitCore for cores that don't support direct routing (like Hysteria2, TUIC, etc.)
|
||||
splitCore = true;
|
||||
preCoreType = enableTun ? ECoreType.sing_box : splitRouteCore;
|
||||
}
|
||||
else if (splitCore)
|
||||
{
|
||||
// User explicitly enabled SplitCore
|
||||
preCoreType = enableTun ? ECoreType.sing_box : splitRouteCore;
|
||||
coreType = pureEndpointCore;
|
||||
|
||||
if (preCoreType == coreType)
|
||||
{
|
||||
preCoreType = null;
|
||||
splitCore = false;
|
||||
}
|
||||
}
|
||||
else if (enableTun) // EnableTun is true but SplitCore is false
|
||||
{
|
||||
// TUN mode handling for Xray/sing_box cores
|
||||
preCoreType = ECoreType.sing_box;
|
||||
|
||||
if (preCoreType == coreType) // CoreType is sing_box
|
||||
{
|
||||
preCoreType = null;
|
||||
}
|
||||
else // CoreType is xray, etc.
|
||||
{
|
||||
// Force SplitCore for non-split cores
|
||||
splitCore = true;
|
||||
}
|
||||
}
|
||||
return (splitCore, coreType, preCoreType);
|
||||
}
|
||||
|
||||
#endregion Core Type
|
||||
}
|
||||
|
|
|
|||
|
|
@ -165,6 +165,13 @@ public class ConfigHandler
|
|||
config.SystemProxyItem.SystemProxyExceptions = Utils.IsWindows() ? Global.SystemProxyExceptionsWindows : Global.SystemProxyExceptionsLinux;
|
||||
}
|
||||
|
||||
config.SplitCoreItem ??= new()
|
||||
{
|
||||
EnableSplitCore = false,
|
||||
SplitCoreTypes = new List<CoreTypeItem>(),
|
||||
RouteCoreType = ECoreType.Xray
|
||||
};
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
|
|
@ -263,6 +270,10 @@ public class ConfigHandler
|
|||
EConfigType.TUIC => await AddTuicServer(config, item),
|
||||
EConfigType.WireGuard => await AddWireguardServer(config, item),
|
||||
EConfigType.Anytls => await AddAnytlsServer(config, item),
|
||||
EConfigType.NaiveProxy => await AddNaiveServer(config, item),
|
||||
EConfigType.Juicity => await AddJuicityServer(config, item),
|
||||
EConfigType.Brook => await AddBrookServer(config, item),
|
||||
EConfigType.Shadowquic => await AddShadowquicServer(config, item),
|
||||
_ => -1,
|
||||
};
|
||||
return ret;
|
||||
|
|
@ -691,7 +702,7 @@ public class ConfigHandler
|
|||
public static async Task<int> AddHysteria2Server(Config config, ProfileItem profileItem, bool toFile = true)
|
||||
{
|
||||
profileItem.ConfigType = EConfigType.Hysteria2;
|
||||
profileItem.CoreType = ECoreType.sing_box;
|
||||
//profileItem.CoreType = ECoreType.sing_box;
|
||||
|
||||
profileItem.Address = profileItem.Address.TrimEx();
|
||||
profileItem.Id = profileItem.Id.TrimEx();
|
||||
|
|
@ -724,16 +735,16 @@ public class ConfigHandler
|
|||
public static async Task<int> AddTuicServer(Config config, ProfileItem profileItem, bool toFile = true)
|
||||
{
|
||||
profileItem.ConfigType = EConfigType.TUIC;
|
||||
profileItem.CoreType = ECoreType.sing_box;
|
||||
//profileItem.CoreType = ECoreType.sing_box;
|
||||
|
||||
profileItem.Address = profileItem.Address.TrimEx();
|
||||
profileItem.Id = profileItem.Id.TrimEx();
|
||||
profileItem.Security = profileItem.Security.TrimEx();
|
||||
profileItem.Network = string.Empty;
|
||||
|
||||
if (!Global.TuicCongestionControls.Contains(profileItem.HeaderType))
|
||||
if (!Global.CongestionControls.Contains(profileItem.HeaderType))
|
||||
{
|
||||
profileItem.HeaderType = Global.TuicCongestionControls.FirstOrDefault()!;
|
||||
profileItem.HeaderType = Global.CongestionControls.FirstOrDefault()!;
|
||||
}
|
||||
|
||||
if (profileItem.StreamSecurity.IsNullOrEmpty())
|
||||
|
|
@ -816,6 +827,144 @@ public class ConfigHandler
|
|||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add or edit a Naive server
|
||||
/// Validates and processes Naive-specific settings
|
||||
/// </summary>
|
||||
/// <param name="config">Current configuration</param>
|
||||
/// <param name="profileItem">Naive profile to add</param>
|
||||
/// <param name="toFile">Whether to save to file</param>
|
||||
/// <returns>0 if successful, -1 if failed</returns>
|
||||
public static async Task<int> AddNaiveServer(Config config, ProfileItem profileItem, bool toFile = true)
|
||||
{
|
||||
profileItem.ConfigType = EConfigType.NaiveProxy;
|
||||
profileItem.CoreType = ECoreType.naiveproxy;
|
||||
|
||||
profileItem.Address = profileItem.Address.TrimEx();
|
||||
profileItem.Id = profileItem.Id.TrimEx();
|
||||
profileItem.Network = string.Empty;
|
||||
if (!Global.NaiveProxyProtocols.Contains(profileItem.HeaderType))
|
||||
{
|
||||
profileItem.HeaderType = Global.NaiveProxyProtocols.FirstOrDefault()!;
|
||||
}
|
||||
if (profileItem.StreamSecurity.IsNullOrEmpty())
|
||||
{
|
||||
profileItem.StreamSecurity = Global.StreamSecurity;
|
||||
}
|
||||
if (profileItem.Id.IsNullOrEmpty())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
await AddServerCommon(config, profileItem, toFile);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add or edit a Juicity server
|
||||
/// Validates and processes Juicity-specific settings
|
||||
/// </summary>
|
||||
/// <param name="config">Current configuration</param>
|
||||
/// <param name="profileItem">Juicity profile to add</param>
|
||||
/// <param name="toFile">Whether to save to file</param>
|
||||
/// <returns>0 if successful, -1 if failed</returns>
|
||||
public static async Task<int> AddJuicityServer(Config config, ProfileItem profileItem, bool toFile = true)
|
||||
{
|
||||
profileItem.ConfigType = EConfigType.Juicity;
|
||||
profileItem.CoreType = ECoreType.juicity;
|
||||
|
||||
profileItem.Address = profileItem.Address.TrimEx();
|
||||
profileItem.Id = profileItem.Id.TrimEx();
|
||||
profileItem.Security = profileItem.Security.TrimEx();
|
||||
profileItem.Network = string.Empty;
|
||||
|
||||
if (!Global.CongestionControls.Contains(profileItem.HeaderType))
|
||||
{
|
||||
profileItem.HeaderType = Global.CongestionControls.FirstOrDefault()!;
|
||||
}
|
||||
|
||||
if (profileItem.StreamSecurity.IsNullOrEmpty())
|
||||
{
|
||||
profileItem.StreamSecurity = Global.StreamSecurity;
|
||||
}
|
||||
if (profileItem.Alpn.IsNullOrEmpty())
|
||||
{
|
||||
profileItem.Alpn = "h3";
|
||||
}
|
||||
if (profileItem.Id.IsNullOrEmpty())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
await AddServerCommon(config, profileItem, toFile);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add or edit a Brook server
|
||||
/// Validates and processes Brook-specific settings
|
||||
/// </summary>
|
||||
/// <param name="config">Current configuration</param>
|
||||
/// <param name="profileItem">Brook profile to add</param>
|
||||
/// <param name="toFile">Whether to save to file</param>
|
||||
/// <returns>0 if successful, -1 if failed</returns>
|
||||
public static async Task<int> AddBrookServer(Config config, ProfileItem profileItem, bool toFile = true)
|
||||
{
|
||||
profileItem.ConfigType = EConfigType.Brook;
|
||||
profileItem.CoreType = ECoreType.brook;
|
||||
|
||||
profileItem.Address = profileItem.Address.TrimEx();
|
||||
profileItem.Id = profileItem.Id.TrimEx();
|
||||
profileItem.Network = string.Empty;
|
||||
if (profileItem.Id.IsNullOrEmpty())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
await AddServerCommon(config, profileItem, toFile);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add or edit a Shadowquic server
|
||||
/// Validates and processes Shadowquic-specific settings
|
||||
/// </summary>
|
||||
/// <param name="config">Current configuration</param>
|
||||
/// <param name="profileItem">Shadowquic profile to add</param>
|
||||
/// <param name="toFile">Whether to save to file</param>
|
||||
/// <returns>0 if successful, -1 if failed</returns>
|
||||
public static async Task<int> AddShadowquicServer(Config config, ProfileItem profileItem, bool toFile = true)
|
||||
{
|
||||
profileItem.ConfigType = EConfigType.Shadowquic;
|
||||
profileItem.CoreType = ECoreType.shadowquic;
|
||||
|
||||
profileItem.Address = profileItem.Address.TrimEx();
|
||||
profileItem.Id = profileItem.Id.TrimEx();
|
||||
profileItem.Security = profileItem.Security.TrimEx();
|
||||
profileItem.Network = string.Empty;
|
||||
|
||||
if (!Global.CongestionControls.Contains(profileItem.HeaderType))
|
||||
{
|
||||
profileItem.HeaderType = Global.CongestionControls.FirstOrDefault()!;
|
||||
}
|
||||
|
||||
if (profileItem.StreamSecurity.IsNullOrEmpty())
|
||||
{
|
||||
profileItem.StreamSecurity = Global.StreamSecurity;
|
||||
}
|
||||
if (profileItem.Alpn.IsNullOrEmpty())
|
||||
{
|
||||
profileItem.Alpn = "h3";
|
||||
}
|
||||
if (profileItem.Id.IsNullOrEmpty())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
await AddServerCommon(config, profileItem, toFile);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sort the server list by the specified column
|
||||
/// Updates the sort order in the profile extension data
|
||||
|
|
@ -1190,43 +1339,6 @@ public class ConfigHandler
|
|||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a SOCKS server profile for pre-SOCKS functionality
|
||||
/// Used when TUN mode is enabled or when a custom config has a pre-SOCKS port
|
||||
/// </summary>
|
||||
/// <param name="config">Current configuration</param>
|
||||
/// <param name="node">Server node that might need pre-SOCKS</param>
|
||||
/// <param name="coreType">Core type being used</param>
|
||||
/// <returns>A SOCKS profile item or null if not needed</returns>
|
||||
public static async Task<ProfileItem?> GetPreSocksItem(Config config, ProfileItem node, ECoreType coreType)
|
||||
{
|
||||
ProfileItem? itemSocks = null;
|
||||
if (node.ConfigType != EConfigType.Custom && coreType != ECoreType.sing_box && config.TunModeItem.EnableTun)
|
||||
{
|
||||
itemSocks = new ProfileItem()
|
||||
{
|
||||
CoreType = ECoreType.sing_box,
|
||||
ConfigType = EConfigType.SOCKS,
|
||||
Address = Global.Loopback,
|
||||
Sni = node.Address, //Tun2SocksAddress
|
||||
Port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks)
|
||||
};
|
||||
}
|
||||
else if ((node.ConfigType == EConfigType.Custom && node.PreSocksPort > 0))
|
||||
{
|
||||
var preCoreType = config.RunningCoreType = config.TunModeItem.EnableTun ? ECoreType.sing_box : ECoreType.Xray;
|
||||
itemSocks = new ProfileItem()
|
||||
{
|
||||
CoreType = preCoreType,
|
||||
ConfigType = EConfigType.SOCKS,
|
||||
Address = Global.Loopback,
|
||||
Port = node.PreSocksPort.Value,
|
||||
};
|
||||
}
|
||||
await Task.CompletedTask;
|
||||
return itemSocks;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove servers with invalid test results (timeout)
|
||||
/// Useful for cleaning up subscription lists
|
||||
|
|
@ -1326,6 +1438,10 @@ public class ConfigHandler
|
|||
EConfigType.TUIC => await AddTuicServer(config, profileItem, false),
|
||||
EConfigType.WireGuard => await AddWireguardServer(config, profileItem, false),
|
||||
EConfigType.Anytls => await AddAnytlsServer(config, profileItem, false),
|
||||
EConfigType.NaiveProxy => await AddNaiveServer(config, profileItem, false),
|
||||
EConfigType.Juicity => await AddJuicityServer(config, profileItem, false),
|
||||
EConfigType.Brook => await AddBrookServer(config, profileItem, false),
|
||||
EConfigType.Shadowquic => await AddShadowquicServer(config, profileItem, false),
|
||||
_ => -1,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ public class CoreAdminHandler
|
|||
{
|
||||
StringBuilder sb = new();
|
||||
sb.AppendLine("#!/bin/bash");
|
||||
var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetBinConfigPath(configPath).AppendQuotes())}";
|
||||
var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetBinConfigPath(configPath, coreInfo.CoreType).AppendQuotes())}";
|
||||
sb.AppendLine($"sudo -S {cmdLine}");
|
||||
var shFilePath = await FileManager.CreateLinuxShellFile("run_as_sudo.sh", sb.ToString(), true);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
using ServiceLib.Services.CoreConfig.Minimal;
|
||||
|
||||
namespace ServiceLib.Handler;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -7,27 +9,26 @@ public class CoreConfigHandler
|
|||
{
|
||||
private static readonly string _tag = "CoreConfigHandler";
|
||||
|
||||
public static async Task<RetResult> GenerateClientConfig(ProfileItem node, string? fileName)
|
||||
public static async Task<RetResult> GenerateClientConfig(CoreLaunchContext context, string? fileName)
|
||||
{
|
||||
var config = AppHandler.Instance.Config;
|
||||
var result = new RetResult();
|
||||
|
||||
if (node.ConfigType == EConfigType.Custom)
|
||||
if (context.ConfigType == EConfigType.Custom)
|
||||
{
|
||||
result = node.CoreType switch
|
||||
{
|
||||
ECoreType.mihomo => await new CoreConfigClashService(config).GenerateClientCustomConfig(node, fileName),
|
||||
ECoreType.sing_box => await new CoreConfigSingboxService(config).GenerateClientCustomConfig(node, fileName),
|
||||
_ => await GenerateClientCustomConfig(node, fileName)
|
||||
};
|
||||
}
|
||||
else if (AppHandler.Instance.GetCoreType(node, node.ConfigType) == ECoreType.sing_box)
|
||||
{
|
||||
result = await new CoreConfigSingboxService(config).GenerateClientConfigContent(node);
|
||||
result = await GetCoreConfigServiceForCustom(context.CoreType).GenerateClientCustomConfig(context.Node, fileName);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = await new CoreConfigV2rayService(config).GenerateClientConfigContent(node);
|
||||
try
|
||||
{
|
||||
result = await GetCoreConfigServiceForClientConfig(context.CoreType).GenerateClientConfigContent(context.Node);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
result.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
if (result.Success != true)
|
||||
{
|
||||
|
|
@ -41,65 +42,44 @@ public class CoreConfigHandler
|
|||
return result;
|
||||
}
|
||||
|
||||
private static async Task<RetResult> GenerateClientCustomConfig(ProfileItem node, string? fileName)
|
||||
public static async Task<RetResult> GeneratePassthroughConfig(CoreLaunchContext context, string? fileName)
|
||||
{
|
||||
var ret = new RetResult();
|
||||
var result = new RetResult();
|
||||
|
||||
try
|
||||
{
|
||||
if (node == null || fileName is null)
|
||||
{
|
||||
ret.Msg = ResUI.CheckServerSettings;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (File.Exists(fileName))
|
||||
{
|
||||
File.SetAttributes(fileName, FileAttributes.Normal); //If the file has a read-only attribute, direct deletion will fail
|
||||
File.Delete(fileName);
|
||||
}
|
||||
|
||||
string addressFileName = node.Address;
|
||||
if (!File.Exists(addressFileName))
|
||||
{
|
||||
addressFileName = Utils.GetConfigPath(addressFileName);
|
||||
}
|
||||
if (!File.Exists(addressFileName))
|
||||
{
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
File.Copy(addressFileName, fileName);
|
||||
File.SetAttributes(fileName, FileAttributes.Normal); //Copy will keep the attributes of addressFileName, so we need to add write permissions to fileName just in case of addressFileName is a read-only file.
|
||||
|
||||
//check again
|
||||
if (!File.Exists(fileName))
|
||||
{
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
|
||||
ret.Success = true;
|
||||
return await Task.FromResult(ret);
|
||||
result = await GetCoreConfigServiceForPassthrough(context.CoreType).GeneratePassthroughConfig(context.Node);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
result.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (result.Success != true)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (fileName.IsNotEmpty() && result.Data != null)
|
||||
{
|
||||
await File.WriteAllTextAsync(fileName, result.Data.ToString());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static async Task<RetResult> GenerateClientSpeedtestConfig(Config config, string fileName, List<ServerTestItem> selecteds, ECoreType coreType)
|
||||
{
|
||||
var result = new RetResult();
|
||||
if (coreType == ECoreType.sing_box)
|
||||
try
|
||||
{
|
||||
result = await new CoreConfigSingboxService(config).GenerateClientSpeedtestConfig(selecteds);
|
||||
result = await GetCoreConfigServiceForMultipleSpeedtest(coreType).GenerateClientSpeedtestConfig(selecteds);
|
||||
}
|
||||
else if (coreType == ECoreType.Xray)
|
||||
catch (Exception ex)
|
||||
{
|
||||
result = await new CoreConfigV2rayService(config).GenerateClientSpeedtestConfig(selecteds);
|
||||
Logging.SaveLog(_tag, ex);
|
||||
result.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return result;
|
||||
}
|
||||
if (result.Success != true)
|
||||
{
|
||||
|
|
@ -109,21 +89,24 @@ public class CoreConfigHandler
|
|||
return result;
|
||||
}
|
||||
|
||||
public static async Task<RetResult> GenerateClientSpeedtestConfig(Config config, ProfileItem node, ServerTestItem testItem, string fileName)
|
||||
public static async Task<RetResult> GenerateClientSpeedtestConfig(Config config, CoreLaunchContext context, ServerTestItem testItem, string fileName)
|
||||
{
|
||||
var result = new RetResult();
|
||||
var initPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.speedtest);
|
||||
var port = Utils.GetFreePort(initPort + testItem.QueueNum);
|
||||
testItem.Port = port;
|
||||
|
||||
if (AppHandler.Instance.GetCoreType(node, node.ConfigType) == ECoreType.sing_box)
|
||||
try
|
||||
{
|
||||
result = await new CoreConfigSingboxService(config).GenerateClientSpeedtestConfig(node, port);
|
||||
result = await GetCoreConfigServiceForSpeedtest(context.CoreType).GenerateClientSpeedtestConfig(context.Node, port);
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
result = await new CoreConfigV2rayService(config).GenerateClientSpeedtestConfig(node, port);
|
||||
Logging.SaveLog(_tag, ex);
|
||||
result.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (result.Success != true)
|
||||
{
|
||||
return result;
|
||||
|
|
@ -140,7 +123,7 @@ public class CoreConfigHandler
|
|||
{
|
||||
result = await new CoreConfigSingboxService(config).GenerateClientMultipleLoadConfig(selecteds);
|
||||
}
|
||||
else
|
||||
else if (coreType == ECoreType.Xray)
|
||||
{
|
||||
result = await new CoreConfigV2rayService(config).GenerateClientMultipleLoadConfig(selecteds, multipleLoad);
|
||||
}
|
||||
|
|
@ -152,4 +135,96 @@ public class CoreConfigHandler
|
|||
await File.WriteAllTextAsync(fileName, result.Data.ToString());
|
||||
return result;
|
||||
}
|
||||
|
||||
private static CoreConfigServiceMinimalBase GetCoreConfigServiceForPassthrough(ECoreType coreType)
|
||||
{
|
||||
switch (coreType)
|
||||
{
|
||||
case ECoreType.sing_box:
|
||||
return new CoreConfigSingboxService(AppHandler.Instance.Config);
|
||||
case ECoreType.Xray:
|
||||
return new CoreConfigV2rayService(AppHandler.Instance.Config);
|
||||
case ECoreType.hysteria2:
|
||||
return new CoreConfigHy2Service(AppHandler.Instance.Config);
|
||||
case ECoreType.naiveproxy:
|
||||
return new CoreConfigNaiveService(AppHandler.Instance.Config);
|
||||
case ECoreType.tuic:
|
||||
return new CoreConfigTuicService(AppHandler.Instance.Config);
|
||||
case ECoreType.juicity:
|
||||
return new CoreConfigJuicityService(AppHandler.Instance.Config);
|
||||
case ECoreType.brook:
|
||||
return new CoreConfigBrookService(AppHandler.Instance.Config);
|
||||
case ECoreType.shadowquic:
|
||||
return new CoreConfigShadowquicService(AppHandler.Instance.Config);
|
||||
default:
|
||||
throw new NotImplementedException($"Core type {coreType} is not implemented for passthrough configuration.");
|
||||
}
|
||||
}
|
||||
|
||||
private static CoreConfigServiceMinimalBase GetCoreConfigServiceForSpeedtest(ECoreType coreType)
|
||||
{
|
||||
switch (coreType)
|
||||
{
|
||||
case ECoreType.sing_box:
|
||||
return new CoreConfigSingboxService(AppHandler.Instance.Config);
|
||||
case ECoreType.Xray:
|
||||
return new CoreConfigV2rayService(AppHandler.Instance.Config);
|
||||
case ECoreType.hysteria2:
|
||||
return new CoreConfigHy2Service(AppHandler.Instance.Config);
|
||||
case ECoreType.naiveproxy:
|
||||
return new CoreConfigNaiveService(AppHandler.Instance.Config);
|
||||
case ECoreType.tuic:
|
||||
return new CoreConfigTuicService(AppHandler.Instance.Config);
|
||||
case ECoreType.juicity:
|
||||
return new CoreConfigJuicityService(AppHandler.Instance.Config);
|
||||
case ECoreType.brook:
|
||||
return new CoreConfigBrookService(AppHandler.Instance.Config);
|
||||
case ECoreType.shadowquic:
|
||||
return new CoreConfigShadowquicService(AppHandler.Instance.Config);
|
||||
default:
|
||||
throw new NotImplementedException($"Core type {coreType} is not implemented for passthrough configuration.");
|
||||
}
|
||||
}
|
||||
|
||||
private static CoreConfigServiceBase GetCoreConfigServiceForMultipleSpeedtest(ECoreType coreType)
|
||||
{
|
||||
switch (coreType)
|
||||
{
|
||||
case ECoreType.sing_box:
|
||||
return new CoreConfigSingboxService(AppHandler.Instance.Config);
|
||||
case ECoreType.Xray:
|
||||
return new CoreConfigV2rayService(AppHandler.Instance.Config);
|
||||
default:
|
||||
throw new NotImplementedException($"Core type {coreType} is not implemented for passthrough configuration.");
|
||||
}
|
||||
}
|
||||
|
||||
private static CoreConfigServiceMinimalBase GetCoreConfigServiceForCustom(ECoreType coreType)
|
||||
{
|
||||
switch (coreType)
|
||||
{
|
||||
case ECoreType.mihomo:
|
||||
return new CoreConfigClashService(AppHandler.Instance.Config);
|
||||
case ECoreType.sing_box:
|
||||
return new CoreConfigSingboxService(AppHandler.Instance.Config);
|
||||
case ECoreType.hysteria2:
|
||||
return new CoreConfigHy2Service(AppHandler.Instance.Config);
|
||||
default:
|
||||
// CoreConfigServiceMinimalBase
|
||||
return new CoreConfigV2rayService(AppHandler.Instance.Config);
|
||||
}
|
||||
}
|
||||
|
||||
private static CoreConfigServiceBase GetCoreConfigServiceForClientConfig(ECoreType coreType)
|
||||
{
|
||||
switch (coreType)
|
||||
{
|
||||
case ECoreType.sing_box:
|
||||
return new CoreConfigSingboxService(AppHandler.Instance.Config);
|
||||
case ECoreType.Xray:
|
||||
return new CoreConfigV2rayService(AppHandler.Instance.Config);
|
||||
default:
|
||||
throw new NotImplementedException($"Core type {coreType} is not implemented for client configuration.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using ServiceLib.Enums;
|
||||
using ServiceLib.Models;
|
||||
using static SQLite.SQLite3;
|
||||
|
||||
namespace ServiceLib.Handler;
|
||||
|
||||
|
|
@ -71,28 +74,23 @@ public class CoreHandler
|
|||
return;
|
||||
}
|
||||
|
||||
var fileName = Utils.GetBinConfigPath(Global.CoreConfigFileName);
|
||||
var result = await CoreConfigHandler.GenerateClientConfig(node, fileName);
|
||||
if (result.Success != true)
|
||||
// Create launch context and configure parameters
|
||||
var context = new CoreLaunchContext(node, _config);
|
||||
context.AdjustForConfigType();
|
||||
|
||||
// Start main core
|
||||
if (!await CoreStart(context))
|
||||
{
|
||||
UpdateFunc(true, result.Msg);
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateFunc(false, $"{node.GetSummary()}");
|
||||
UpdateFunc(false, $"{Utils.GetRuntimeInfo()}");
|
||||
UpdateFunc(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
|
||||
await CoreStop();
|
||||
await Task.Delay(100);
|
||||
|
||||
if (Utils.IsWindows() && _config.TunModeItem.EnableTun)
|
||||
// Start pre-core if needed
|
||||
if (!await CoreStartPreService(context))
|
||||
{
|
||||
await Task.Delay(100);
|
||||
await WindowsUtils.RemoveTunDevice();
|
||||
await CoreStop(); // Clean up main core if pre-core fails
|
||||
return;
|
||||
}
|
||||
|
||||
await CoreStart(node);
|
||||
await CoreStartPreService(node);
|
||||
if (_process != null)
|
||||
{
|
||||
UpdateFunc(true, $"{node.GetSummary()}");
|
||||
|
|
@ -103,7 +101,7 @@ public class CoreHandler
|
|||
{
|
||||
var coreType = selecteds.Exists(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls) ? ECoreType.sing_box : ECoreType.Xray;
|
||||
var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false));
|
||||
var configPath = Utils.GetBinConfigPath(fileName);
|
||||
var configPath = Utils.GetBinConfigPath(fileName, coreType);
|
||||
var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, configPath, selecteds, coreType);
|
||||
UpdateFunc(false, result.Msg);
|
||||
if (result.Success != true)
|
||||
|
|
@ -132,15 +130,17 @@ public class CoreHandler
|
|||
return -1;
|
||||
}
|
||||
|
||||
var context = new CoreLaunchContext(node, _config);
|
||||
context.AdjustForConfigType();
|
||||
var coreType = context.CoreType;
|
||||
var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false));
|
||||
var configPath = Utils.GetBinConfigPath(fileName);
|
||||
var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, node, testItem, configPath);
|
||||
var configPath = Utils.GetBinConfigPath(fileName, coreType);
|
||||
var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, context, testItem, configPath);
|
||||
if (result.Success != true)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
var coreType = AppHandler.Instance.GetCoreType(node, node.ConfigType);
|
||||
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType);
|
||||
var proc = await RunProcess(coreInfo, fileName, true, false);
|
||||
if (proc is null)
|
||||
|
|
@ -181,43 +181,84 @@ public class CoreHandler
|
|||
|
||||
#region Private
|
||||
|
||||
private async Task CoreStart(ProfileItem node)
|
||||
private async Task<bool> CoreStart(CoreLaunchContext context)
|
||||
{
|
||||
var coreType = _config.RunningCoreType = AppHandler.Instance.GetCoreType(node, node.ConfigType);
|
||||
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType);
|
||||
var coreType = context.SplitCore ? context.PureEndpointCore : context.CoreType;
|
||||
var fileName = Utils.GetBinConfigPath(Global.CoreConfigFileName, coreType);
|
||||
var result = context.SplitCore
|
||||
? await CoreConfigHandler.GeneratePassthroughConfig(context, fileName)
|
||||
: await CoreConfigHandler.GenerateClientConfig(context, fileName);
|
||||
|
||||
if (result.Success != true)
|
||||
{
|
||||
UpdateFunc(true, result.Msg);
|
||||
return false;
|
||||
}
|
||||
|
||||
UpdateFunc(false, $"{context.Node.GetSummary()}");
|
||||
UpdateFunc(false, $"{Utils.GetRuntimeInfo()}");
|
||||
UpdateFunc(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
|
||||
|
||||
await CoreStop();
|
||||
await Task.Delay(100);
|
||||
|
||||
if (Utils.IsWindows() && _config.TunModeItem.EnableTun)
|
||||
{
|
||||
await Task.Delay(100);
|
||||
await WindowsUtils.RemoveTunDevice();
|
||||
}
|
||||
|
||||
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(context.CoreType);
|
||||
var displayLog = context.Node.ConfigType != EConfigType.Custom || context.Node.DisplayLog;
|
||||
var proc = await RunProcess(coreInfo, Utils.GetBinConfigFileName(Global.CoreConfigFileName, coreType), displayLog, true);
|
||||
|
||||
var displayLog = node.ConfigType != EConfigType.Custom || node.DisplayLog;
|
||||
var proc = await RunProcess(coreInfo, Global.CoreConfigFileName, displayLog, true);
|
||||
if (proc is null)
|
||||
{
|
||||
return;
|
||||
UpdateFunc(true, ResUI.FailedToRunCore);
|
||||
return false;
|
||||
}
|
||||
|
||||
_process = proc;
|
||||
_config.RunningCoreType = (ECoreType)(context.PreCoreType != null ? context.PreCoreType : coreType);
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task CoreStartPreService(ProfileItem node)
|
||||
private async Task<bool> CoreStartPreService(CoreLaunchContext context)
|
||||
{
|
||||
if (_process != null && !_process.HasExited)
|
||||
if (context.PreCoreType == null)
|
||||
{
|
||||
var coreType = AppHandler.Instance.GetCoreType(node, node.ConfigType);
|
||||
var itemSocks = await ConfigHandler.GetPreSocksItem(_config, node, coreType);
|
||||
if (itemSocks != null)
|
||||
{
|
||||
var preCoreType = itemSocks.CoreType ?? ECoreType.sing_box;
|
||||
var fileName = Utils.GetBinConfigPath(Global.CorePreConfigFileName);
|
||||
var result = await CoreConfigHandler.GenerateClientConfig(itemSocks, fileName);
|
||||
if (result.Success)
|
||||
{
|
||||
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(preCoreType);
|
||||
var proc = await RunProcess(coreInfo, Global.CorePreConfigFileName, true, true);
|
||||
if (proc is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_processPre = proc;
|
||||
}
|
||||
}
|
||||
return true; // No pre-core needed, consider successful
|
||||
}
|
||||
|
||||
var fileName = Utils.GetBinConfigPath(Global.CorePreConfigFileName, (ECoreType)context.PreCoreType);
|
||||
var itemSocks = new ProfileItem()
|
||||
{
|
||||
CoreType = context.PreCoreType,
|
||||
ConfigType = EConfigType.SOCKS,
|
||||
Address = Global.Loopback,
|
||||
Sni = context.EnableTun && Utils.IsDomain(context.Node.Address) ? context.Node.Address : string.Empty, //Tun2SocksAddress
|
||||
Port = context.PreSocksPort
|
||||
};
|
||||
var itemSocksLaunch = new CoreLaunchContext(itemSocks, _config);
|
||||
|
||||
var result = await CoreConfigHandler.GenerateClientConfig(itemSocksLaunch, fileName);
|
||||
if (!result.Success)
|
||||
{
|
||||
UpdateFunc(true, result.Msg);
|
||||
return false;
|
||||
}
|
||||
|
||||
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo((ECoreType)context.PreCoreType);
|
||||
var proc = await RunProcess(coreInfo, Utils.GetBinConfigFileName(Global.CorePreConfigFileName, (ECoreType)context.PreCoreType), true, true);
|
||||
|
||||
if (proc is null || (_process?.HasExited == true))
|
||||
{
|
||||
UpdateFunc(true, ResUI.FailedToRunCore);
|
||||
return false;
|
||||
}
|
||||
|
||||
_processPre = proc;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void UpdateFunc(bool notify, string msg)
|
||||
|
|
@ -267,7 +308,7 @@ public class CoreHandler
|
|||
StartInfo = new()
|
||||
{
|
||||
FileName = fileName,
|
||||
Arguments = string.Format(coreInfo.Arguments, coreInfo.AbsolutePath ? Utils.GetBinConfigPath(configPath).AppendQuotes() : configPath),
|
||||
Arguments = string.Format(coreInfo.Arguments, coreInfo.AbsolutePath ? Utils.GetBinConfigPath(configPath, coreInfo.CoreType).AppendQuotes() : configPath),
|
||||
WorkingDirectory = Utils.GetBinConfigPath(),
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = displayLog,
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ public sealed class CoreInfoHandler
|
|||
{
|
||||
CoreType = ECoreType.hysteria,
|
||||
CoreExes = ["hysteria"],
|
||||
Arguments = "",
|
||||
Arguments = "-c {0}",
|
||||
Url = GetCoreUrl(ECoreType.hysteria),
|
||||
},
|
||||
|
||||
|
|
@ -180,7 +180,7 @@ public sealed class CoreInfoHandler
|
|||
{
|
||||
CoreType = ECoreType.hysteria2,
|
||||
CoreExes = ["hysteria-windows-amd64", "hysteria-linux-amd64", "hysteria"],
|
||||
Arguments = "",
|
||||
Arguments = "-c {0}",
|
||||
Url = GetCoreUrl(ECoreType.hysteria2),
|
||||
},
|
||||
|
||||
|
|
|
|||
49
v2rayN/ServiceLib/Handler/Fmt/BrookFmt.cs
Normal file
49
v2rayN/ServiceLib/Handler/Fmt/BrookFmt.cs
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
using static QRCoder.PayloadGenerator;
|
||||
|
||||
namespace ServiceLib.Handler.Fmt;
|
||||
public class BrookFmt : BaseFmt
|
||||
{
|
||||
public static ProfileItem? Resolve(string str, out string msg)
|
||||
{
|
||||
msg = ResUI.ConfigurationFormatIncorrect;
|
||||
|
||||
var parsedUrl = Utils.TryUri(str);
|
||||
if (parsedUrl == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ProfileItem item = new()
|
||||
{
|
||||
ConfigType = EConfigType.Brook,
|
||||
Remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped),
|
||||
Address = parsedUrl.IdnHost,
|
||||
Port = parsedUrl.Port,
|
||||
};
|
||||
var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo);
|
||||
item.Id = rawUserInfo;
|
||||
|
||||
var query = Utils.ParseQueryString(parsedUrl.Query);
|
||||
_ = ResolveStdTransport(query, ref item);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public static string? ToUri(ProfileItem? item)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var remark = string.Empty;
|
||||
if (item.Remarks.IsNotEmpty())
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.Remarks);
|
||||
}
|
||||
var pw = item.Id;
|
||||
var dicQuery = new Dictionary<string, string>();
|
||||
_ = GetStdTransport(item, Global.None, ref dicQuery);
|
||||
|
||||
return ToUri(EConfigType.Brook, item.Address, item.Port, pw, dicQuery, remark);
|
||||
}
|
||||
}
|
||||
|
|
@ -19,6 +19,10 @@ public class FmtHandler
|
|||
EConfigType.TUIC => TuicFmt.ToUri(item),
|
||||
EConfigType.WireGuard => WireguardFmt.ToUri(item),
|
||||
EConfigType.Anytls => AnytlsFmt.ToUri(item),
|
||||
EConfigType.NaiveProxy => NaiveFmt.ToUri(item),
|
||||
EConfigType.Juicity => JuicityFmt.ToUri(item),
|
||||
EConfigType.Brook => BrookFmt.ToUri(item),
|
||||
EConfigType.Shadowquic => ShadowquicFmt.ToUri(item),
|
||||
_ => null,
|
||||
};
|
||||
|
||||
|
|
@ -80,6 +84,22 @@ public class FmtHandler
|
|||
{
|
||||
return AnytlsFmt.Resolve(str, out msg);
|
||||
}
|
||||
else if (str.StartsWith(Global.ProtocolShares[EConfigType.NaiveProxy]))
|
||||
{
|
||||
return NaiveFmt.Resolve(str, out msg);
|
||||
}
|
||||
else if (str.StartsWith(Global.ProtocolShares[EConfigType.Juicity]))
|
||||
{
|
||||
return JuicityFmt.Resolve(str, out msg);
|
||||
}
|
||||
else if (str.StartsWith(Global.ProtocolShares[EConfigType.Brook]))
|
||||
{
|
||||
return BrookFmt.Resolve(str, out msg);
|
||||
}
|
||||
else if (str.StartsWith(Global.ProtocolShares[EConfigType.Shadowquic]))
|
||||
{
|
||||
return ShadowquicFmt.Resolve(str, out msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = ResUI.NonvmessOrssProtocol;
|
||||
|
|
|
|||
63
v2rayN/ServiceLib/Handler/Fmt/JuicityFmt.cs
Normal file
63
v2rayN/ServiceLib/Handler/Fmt/JuicityFmt.cs
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
namespace ServiceLib.Handler.Fmt;
|
||||
|
||||
public class JuicityFmt : BaseFmt
|
||||
{
|
||||
public static ProfileItem? Resolve(string str, out string msg)
|
||||
{
|
||||
msg = ResUI.ConfigurationFormatIncorrect;
|
||||
|
||||
ProfileItem item = new()
|
||||
{
|
||||
ConfigType = EConfigType.Juicity
|
||||
};
|
||||
|
||||
var url = Utils.TryUri(str);
|
||||
if (url == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
item.Address = url.IdnHost;
|
||||
item.Port = url.Port;
|
||||
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
||||
var rawUserInfo = Utils.UrlDecode(url.UserInfo);
|
||||
var userInfoParts = rawUserInfo.Split(new[] { ':' }, 2);
|
||||
if (userInfoParts.Length == 2)
|
||||
{
|
||||
item.Id = userInfoParts.First();
|
||||
item.Security = userInfoParts.Last();
|
||||
}
|
||||
|
||||
var query = Utils.ParseQueryString(url.Query);
|
||||
ResolveStdTransport(query, ref item);
|
||||
item.HeaderType = query["congestion_control"] ?? "";
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public static string? ToUri(ProfileItem? item)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var remark = string.Empty;
|
||||
if (item.Remarks.IsNotEmpty())
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.Remarks);
|
||||
}
|
||||
var dicQuery = new Dictionary<string, string>();
|
||||
if (item.Sni.IsNotEmpty())
|
||||
{
|
||||
dicQuery.Add("sni", item.Sni);
|
||||
}
|
||||
if (item.Alpn.IsNotEmpty())
|
||||
{
|
||||
dicQuery.Add("alpn", Utils.UrlEncode(item.Alpn));
|
||||
}
|
||||
dicQuery.Add("congestion_control", item.HeaderType);
|
||||
|
||||
return ToUri(EConfigType.Juicity, item.Address, item.Port, $"{item.Id}:{item.Security}", dicQuery, remark);
|
||||
}
|
||||
}
|
||||
52
v2rayN/ServiceLib/Handler/Fmt/NaiveFmt.cs
Normal file
52
v2rayN/ServiceLib/Handler/Fmt/NaiveFmt.cs
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
using static QRCoder.PayloadGenerator;
|
||||
|
||||
namespace ServiceLib.Handler.Fmt;
|
||||
public class NaiveFmt : BaseFmt
|
||||
{
|
||||
public static ProfileItem? Resolve(string str, out string msg)
|
||||
{
|
||||
msg = ResUI.ConfigurationFormatIncorrect;
|
||||
|
||||
var parsedUrl = Utils.TryUri(str);
|
||||
if (parsedUrl == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ProfileItem item = new()
|
||||
{
|
||||
ConfigType = EConfigType.NaiveProxy,
|
||||
Remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped),
|
||||
Address = parsedUrl.IdnHost,
|
||||
Port = parsedUrl.Port,
|
||||
};
|
||||
var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo);
|
||||
item.Id = rawUserInfo;
|
||||
|
||||
var query = Utils.ParseQueryString(parsedUrl.Query);
|
||||
_ = ResolveStdTransport(query, ref item);
|
||||
|
||||
item.HeaderType = query["protocol"] ?? "";
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public static string? ToUri(ProfileItem? item)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var remark = string.Empty;
|
||||
if (item.Remarks.IsNotEmpty())
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.Remarks);
|
||||
}
|
||||
var pw = item.Id;
|
||||
var dicQuery = new Dictionary<string, string>();
|
||||
_ = GetStdTransport(item, Global.None, ref dicQuery);
|
||||
dicQuery.Add("protocol", item.HeaderType);
|
||||
|
||||
return ToUri(EConfigType.NaiveProxy, item.Address, item.Port, pw, dicQuery, remark);
|
||||
}
|
||||
}
|
||||
63
v2rayN/ServiceLib/Handler/Fmt/ShadowquicFmt.cs
Normal file
63
v2rayN/ServiceLib/Handler/Fmt/ShadowquicFmt.cs
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
namespace ServiceLib.Handler.Fmt;
|
||||
|
||||
public class ShadowquicFmt : BaseFmt
|
||||
{
|
||||
public static ProfileItem? Resolve(string str, out string msg)
|
||||
{
|
||||
msg = ResUI.ConfigurationFormatIncorrect;
|
||||
|
||||
ProfileItem item = new()
|
||||
{
|
||||
ConfigType = EConfigType.Shadowquic
|
||||
};
|
||||
|
||||
var url = Utils.TryUri(str);
|
||||
if (url == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
item.Address = url.IdnHost;
|
||||
item.Port = url.Port;
|
||||
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
||||
var rawUserInfo = Utils.UrlDecode(url.UserInfo);
|
||||
var userInfoParts = rawUserInfo.Split(new[] { ':' }, 2);
|
||||
if (userInfoParts.Length == 2)
|
||||
{
|
||||
item.Id = userInfoParts.First();
|
||||
item.Security = userInfoParts.Last();
|
||||
}
|
||||
|
||||
var query = Utils.ParseQueryString(url.Query);
|
||||
ResolveStdTransport(query, ref item);
|
||||
item.HeaderType = query["congestion_control"] ?? "";
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public static string? ToUri(ProfileItem? item)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var remark = string.Empty;
|
||||
if (item.Remarks.IsNotEmpty())
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.Remarks);
|
||||
}
|
||||
var dicQuery = new Dictionary<string, string>();
|
||||
if (item.Sni.IsNotEmpty())
|
||||
{
|
||||
dicQuery.Add("sni", item.Sni);
|
||||
}
|
||||
if (item.Alpn.IsNotEmpty())
|
||||
{
|
||||
dicQuery.Add("alpn", Utils.UrlEncode(item.Alpn));
|
||||
}
|
||||
dicQuery.Add("congestion_control", item.HeaderType);
|
||||
|
||||
return ToUri(EConfigType.Shadowquic, item.Address, item.Port, $"{item.Id}:{item.Security}", dicQuery, remark);
|
||||
}
|
||||
}
|
||||
|
|
@ -48,6 +48,7 @@ public class Config
|
|||
public List<InItem> Inbound { get; set; }
|
||||
public List<KeyEventItem> GlobalHotkeys { get; set; }
|
||||
public List<CoreTypeItem> CoreTypeItem { get; set; }
|
||||
public SplitCoreItem SplitCoreItem { get; set; }
|
||||
|
||||
#endregion other entities
|
||||
}
|
||||
|
|
|
|||
|
|
@ -138,6 +138,14 @@ public class CoreTypeItem
|
|||
public ECoreType CoreType { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class SplitCoreItem
|
||||
{
|
||||
public bool EnableSplitCore { get; set; }
|
||||
public List<CoreTypeItem> SplitCoreTypes { get; set; }
|
||||
public ECoreType RouteCoreType { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class TunModeItem
|
||||
{
|
||||
|
|
|
|||
53
v2rayN/ServiceLib/Models/CoreLaunchContext.cs
Normal file
53
v2rayN/ServiceLib/Models/CoreLaunchContext.cs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
namespace ServiceLib.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Core launch context that encapsulates all parameters required for launching
|
||||
/// </summary>
|
||||
public class CoreLaunchContext
|
||||
{
|
||||
public ProfileItem Node { get; set; }
|
||||
public bool SplitCore { get; set; }
|
||||
public ECoreType CoreType { get; set; }
|
||||
public ECoreType? PreCoreType { get; set; }
|
||||
public ECoreType PureEndpointCore { get; set; }
|
||||
public ECoreType SplitRouteCore { get; set; }
|
||||
public bool EnableTun { get; set; }
|
||||
public int PreSocksPort { get; set; }
|
||||
public EConfigType ConfigType { get; set; }
|
||||
|
||||
public CoreLaunchContext(ProfileItem node, Config config)
|
||||
{
|
||||
Node = node;
|
||||
SplitCore = config.SplitCoreItem.EnableSplitCore;
|
||||
CoreType = AppHandler.Instance.GetCoreType(node, node.ConfigType);
|
||||
PureEndpointCore = AppHandler.Instance.GetSplitCoreType(node, node.ConfigType);
|
||||
SplitRouteCore = config.SplitCoreItem.RouteCoreType;
|
||||
EnableTun = config.TunModeItem.EnableTun;
|
||||
PreSocksPort = 0;
|
||||
PreCoreType = null;
|
||||
ConfigType = node.ConfigType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adjust context parameters based on configuration type
|
||||
/// </summary>
|
||||
public void AdjustForConfigType()
|
||||
{
|
||||
(SplitCore, CoreType, PreCoreType) = AppHandler.Instance.GetCoreAndPreType(Node);
|
||||
if (Node.ConfigType == EConfigType.Custom)
|
||||
{
|
||||
if (Node.PreSocksPort > 0)
|
||||
{
|
||||
PreSocksPort = Node.PreSocksPort.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
EnableTun = false;
|
||||
}
|
||||
}
|
||||
else if (PreCoreType != null)
|
||||
{
|
||||
PreSocksPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.split);
|
||||
}
|
||||
}
|
||||
}
|
||||
72
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
72
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
|
|
@ -663,6 +663,15 @@ namespace ServiceLib.Resx {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Add [Brook] Configuration 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuAddBrookServer {
|
||||
get {
|
||||
return ResourceManager.GetString("menuAddBrookServer", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Add a custom configuration Configuration 的本地化字符串。
|
||||
/// </summary>
|
||||
|
|
@ -690,6 +699,24 @@ namespace ServiceLib.Resx {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Add [Juicity] Configuration 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuAddJuicityServer {
|
||||
get {
|
||||
return ResourceManager.GetString("menuAddJuicityServer", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Add [Naive] Configuration 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuAddNaiveServer {
|
||||
get {
|
||||
return ResourceManager.GetString("menuAddNaiveServer", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Import Share Links from clipboard (Ctrl+V) 的本地化字符串。
|
||||
/// </summary>
|
||||
|
|
@ -717,6 +744,15 @@ namespace ServiceLib.Resx {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Add [Shadowquic] Configuration 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuAddShadowquicServer {
|
||||
get {
|
||||
return ResourceManager.GetString("menuAddShadowquicServer", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Add [Shadowsocks] Configuration 的本地化字符串。
|
||||
/// </summary>
|
||||
|
|
@ -2472,6 +2508,15 @@ namespace ServiceLib.Resx {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Proxy Protocol 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbHeaderType100 {
|
||||
get {
|
||||
return ResourceManager.GetString("TbHeaderType100", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Congestion control 的本地化字符串。
|
||||
/// </summary>
|
||||
|
|
@ -3426,6 +3471,33 @@ namespace ServiceLib.Resx {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Outbound and routing are decoupled. If their Core types differ, two separate cores will be activated. 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSettingsSplitCoreDoc1 {
|
||||
get {
|
||||
return ResourceManager.GetString("TbSettingsSplitCoreDoc1", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Routing Core defaults to sing-box when Tun is enabled. 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSettingsSplitCoreDoc2 {
|
||||
get {
|
||||
return ResourceManager.GetString("TbSettingsSplitCoreDoc2", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Enable separation of outbound and routing cores 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSettingsSplitCoreEnable {
|
||||
get {
|
||||
return ResourceManager.GetString("TbSettingsSplitCoreEnable", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 sing-box ruleset files source (optional) 的本地化字符串。
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -1404,4 +1404,28 @@
|
|||
<data name="menuAddAnytlsServer" xml:space="preserve">
|
||||
<value>Add [Anytls] Configuration</value>
|
||||
</data>
|
||||
<data name="TbSettingsSplitCoreEnable" xml:space="preserve">
|
||||
<value>Enable separation of outbound and routing cores</value>
|
||||
</data>
|
||||
<data name="TbSettingsSplitCoreDoc1" xml:space="preserve">
|
||||
<value>Outbound and routing are decoupled. If their Core types differ, two separate cores will be activated.</value>
|
||||
</data>
|
||||
<data name="TbSettingsSplitCoreDoc2" xml:space="preserve">
|
||||
<value>Routing Core defaults to sing-box when Tun is enabled.</value>
|
||||
</data>
|
||||
<data name="menuAddBrookServer" xml:space="preserve">
|
||||
<value>افزودن سرور [Brook]</value>
|
||||
</data>
|
||||
<data name="menuAddJuicityServer" xml:space="preserve">
|
||||
<value>افزودن سرور [Juicity]</value>
|
||||
</data>
|
||||
<data name="menuAddNaiveServer" xml:space="preserve">
|
||||
<value>افزودن سرور [Naive]</value>
|
||||
</data>
|
||||
<data name="menuAddShadowquicServer" xml:space="preserve">
|
||||
<value>افزودن سرور [Shadowquic]</value>
|
||||
</data>
|
||||
<data name="TbHeaderType100" xml:space="preserve">
|
||||
<value>Proxy Protocol</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -1404,4 +1404,28 @@
|
|||
<data name="menuAddAnytlsServer" xml:space="preserve">
|
||||
<value>[Anytls] konfiguráció hozzáadása</value>
|
||||
</data>
|
||||
<data name="TbSettingsSplitCoreEnable" xml:space="preserve">
|
||||
<value>Enable separation of outbound and routing cores</value>
|
||||
</data>
|
||||
<data name="TbSettingsSplitCoreDoc1" xml:space="preserve">
|
||||
<value>Outbound and routing are decoupled. If their Core types differ, two separate cores will be activated.</value>
|
||||
</data>
|
||||
<data name="TbSettingsSplitCoreDoc2" xml:space="preserve">
|
||||
<value>Routing Core defaults to sing-box when Tun is enabled.</value>
|
||||
</data>
|
||||
<data name="menuAddBrookServer" xml:space="preserve">
|
||||
<value>[Brook] szerver hozzáadása</value>
|
||||
</data>
|
||||
<data name="menuAddJuicityServer" xml:space="preserve">
|
||||
<value>[Juicity] szerver hozzáadása</value>
|
||||
</data>
|
||||
<data name="menuAddNaiveServer" xml:space="preserve">
|
||||
<value>[Naive] szerver hozzáadása</value>
|
||||
</data>
|
||||
<data name="menuAddShadowquicServer" xml:space="preserve">
|
||||
<value>[Shadowquic] szerver hozzáadása</value>
|
||||
</data>
|
||||
<data name="TbHeaderType100" xml:space="preserve">
|
||||
<value>Proxy Protocol</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -1404,4 +1404,28 @@
|
|||
<data name="menuAddAnytlsServer" xml:space="preserve">
|
||||
<value>Add [Anytls] Configuration</value>
|
||||
</data>
|
||||
<data name="TbSettingsSplitCoreEnable" xml:space="preserve">
|
||||
<value>Enable separation of outbound and routing cores</value>
|
||||
</data>
|
||||
<data name="TbSettingsSplitCoreDoc1" xml:space="preserve">
|
||||
<value>Outbound and routing are decoupled. If their Core types differ, two separate cores will be activated.</value>
|
||||
</data>
|
||||
<data name="TbSettingsSplitCoreDoc2" xml:space="preserve">
|
||||
<value>Routing Core defaults to sing-box when Tun is enabled.</value>
|
||||
</data>
|
||||
<data name="menuAddBrookServer" xml:space="preserve">
|
||||
<value>Add [Brook] Configuration</value>
|
||||
</data>
|
||||
<data name="menuAddJuicityServer" xml:space="preserve">
|
||||
<value>Add [Juicity] Configuration</value>
|
||||
</data>
|
||||
<data name="menuAddNaiveServer" xml:space="preserve">
|
||||
<value>Add [Naive] Configuration</value>
|
||||
</data>
|
||||
<data name="menuAddShadowquicServer" xml:space="preserve">
|
||||
<value>Add [Shadowquic] Configuration</value>
|
||||
</data>
|
||||
<data name="TbHeaderType100" xml:space="preserve">
|
||||
<value>Proxy Protocol</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -1404,4 +1404,28 @@
|
|||
<data name="menuAddAnytlsServer" xml:space="preserve">
|
||||
<value>Добавить сервер [Anytls]</value>
|
||||
</data>
|
||||
<data name="TbSettingsSplitCoreEnable" xml:space="preserve">
|
||||
<value>Enable separation of outbound and routing cores</value>
|
||||
</data>
|
||||
<data name="TbSettingsSplitCoreDoc1" xml:space="preserve">
|
||||
<value>Outbound and routing are decoupled. If their Core types differ, two separate cores will be activated.</value>
|
||||
</data>
|
||||
<data name="TbSettingsSplitCoreDoc2" xml:space="preserve">
|
||||
<value>Routing Core defaults to sing-box when Tun is enabled.</value>
|
||||
</data>
|
||||
<data name="menuAddBrookServer" xml:space="preserve">
|
||||
<value>Добавить сервер [Brook]</value>
|
||||
</data>
|
||||
<data name="menuAddJuicityServer" xml:space="preserve">
|
||||
<value>Добавить сервер [Juicity]</value>
|
||||
</data>
|
||||
<data name="menuAddNaiveServer" xml:space="preserve">
|
||||
<value>Добавить сервер [Naive]</value>
|
||||
</data>
|
||||
<data name="menuAddShadowquicServer" xml:space="preserve">
|
||||
<value>Добавить сервер [Shadowquic]</value>
|
||||
</data>
|
||||
<data name="TbHeaderType100" xml:space="preserve">
|
||||
<value>Proxy Protocol</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -1401,4 +1401,28 @@
|
|||
<data name="menuAddAnytlsServer" xml:space="preserve">
|
||||
<value>添加 [Anytls] 配置文件</value>
|
||||
</data>
|
||||
<data name="TbSettingsSplitCoreEnable" xml:space="preserve">
|
||||
<value>启用出站核心与路由核心分离</value>
|
||||
</data>
|
||||
<data name="TbSettingsSplitCoreDoc1" xml:space="preserve">
|
||||
<value>出站与路由分离,出站与分流 Core 类型不同时,将启用两个核心</value>
|
||||
</data>
|
||||
<data name="TbSettingsSplitCoreDoc2" xml:space="preserve">
|
||||
<value>启用 Tun 时,路由 Core 为 sing-box</value>
|
||||
</data>
|
||||
<data name="menuAddBrookServer" xml:space="preserve">
|
||||
<value>添加 [Brook] 配置文件</value>
|
||||
</data>
|
||||
<data name="menuAddJuicityServer" xml:space="preserve">
|
||||
<value>添加 [Juicity] 配置文件</value>
|
||||
</data>
|
||||
<data name="menuAddNaiveServer" xml:space="preserve">
|
||||
<value>添加 [Naive] 配置文件</value>
|
||||
</data>
|
||||
<data name="menuAddShadowquicServer" xml:space="preserve">
|
||||
<value>添加 [Shadowquic] 配置文件</value>
|
||||
</data>
|
||||
<data name="TbHeaderType100" xml:space="preserve">
|
||||
<value>代理协议</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -1401,4 +1401,28 @@
|
|||
<data name="menuAddAnytlsServer" xml:space="preserve">
|
||||
<value>新增 [Anytls] 設定檔</value>
|
||||
</data>
|
||||
<data name="TbSettingsSplitCoreEnable" xml:space="preserve">
|
||||
<value>Enable separation of outbound and routing cores</value>
|
||||
</data>
|
||||
<data name="TbSettingsSplitCoreDoc1" xml:space="preserve">
|
||||
<value>Outbound and routing are decoupled. If their Core types differ, two separate cores will be activated.</value>
|
||||
</data>
|
||||
<data name="TbSettingsSplitCoreDoc2" xml:space="preserve">
|
||||
<value>Routing Core defaults to sing-box when Tun is enabled.</value>
|
||||
</data>
|
||||
<data name="menuAddBrookServer" xml:space="preserve">
|
||||
<value>新增 [Brook] 設定檔</value>
|
||||
</data>
|
||||
<data name="menuAddJuicityServer" xml:space="preserve">
|
||||
<value>新增 [Juicity] 設定檔</value>
|
||||
</data>
|
||||
<data name="menuAddNaiveServer" xml:space="preserve">
|
||||
<value>新增 [Naive] 設定檔</value>
|
||||
</data>
|
||||
<data name="menuAddShadowquicServer" xml:space="preserve">
|
||||
<value>新增 [Shadowquic] 設定檔</value>
|
||||
</data>
|
||||
<data name="TbHeaderType100" xml:space="preserve">
|
||||
<value>Proxy Protocol</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using YamlDotNet.Core.Tokens;
|
||||
|
||||
namespace ServiceLib.Services.CoreConfig;
|
||||
|
||||
public abstract class CoreConfigServiceMinimalBase(Config config)
|
||||
{
|
||||
public virtual string _tag => GetType().Name;
|
||||
protected Config _config = config;
|
||||
|
||||
public virtual Task<RetResult> GeneratePassthroughConfig(ProfileItem node)
|
||||
{
|
||||
return GeneratePassthroughConfig(node, AppHandler.Instance.GetLocalPort(EInboundProtocol.split));
|
||||
}
|
||||
public virtual Task<RetResult> GenerateClientSpeedtestConfig(ProfileItem node, int port)
|
||||
{
|
||||
return GeneratePassthroughConfig(node, port);
|
||||
}
|
||||
protected abstract Task<RetResult> GeneratePassthroughConfig(ProfileItem node, int port);
|
||||
public virtual async Task<RetResult> GenerateClientCustomConfig(ProfileItem node, string? fileName)
|
||||
{
|
||||
var ret = new RetResult();
|
||||
try
|
||||
{
|
||||
if (node == null || fileName is null)
|
||||
{
|
||||
ret.Msg = ResUI.CheckServerSettings;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (File.Exists(fileName))
|
||||
{
|
||||
File.SetAttributes(fileName, FileAttributes.Normal); //If the file has a read-only attribute, direct deletion will fail
|
||||
File.Delete(fileName);
|
||||
}
|
||||
|
||||
string addressFileName = node.Address;
|
||||
if (!File.Exists(addressFileName))
|
||||
{
|
||||
addressFileName = Utils.GetConfigPath(addressFileName);
|
||||
}
|
||||
if (!File.Exists(addressFileName))
|
||||
{
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
File.Copy(addressFileName, fileName);
|
||||
File.SetAttributes(fileName, FileAttributes.Normal); //Copy will keep the attributes of addressFileName, so we need to add write permissions to fileName just in case of addressFileName is a read-only file.
|
||||
|
||||
//check again
|
||||
if (!File.Exists(fileName))
|
||||
{
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
|
||||
ret.Success = true;
|
||||
return await Task.FromResult(ret);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class CoreConfigServiceBase(Config config) : CoreConfigServiceMinimalBase(config)
|
||||
{
|
||||
public abstract Task<RetResult> GenerateClientConfigContent(ProfileItem node);
|
||||
public abstract Task<RetResult> GenerateClientMultipleLoadConfig(List<ProfileItem> selecteds, EMultipleLoad multipleLoad);
|
||||
public virtual Task<RetResult> GenerateClientMultipleLoadConfig(List<ProfileItem> selecteds)
|
||||
{
|
||||
return GenerateClientMultipleLoadConfig(selecteds, EMultipleLoad.LeastPing);
|
||||
}
|
||||
public abstract Task<RetResult> GenerateClientSpeedtestConfig(List<ServerTestItem> selecteds);
|
||||
}
|
||||
|
|
@ -1,25 +1,19 @@
|
|||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Reactive;
|
||||
using System.Text.Json.Nodes;
|
||||
using DynamicData;
|
||||
using ServiceLib.Models;
|
||||
|
||||
namespace ServiceLib.Services.CoreConfig;
|
||||
|
||||
public class CoreConfigSingboxService
|
||||
public class CoreConfigSingboxService(Config config) : CoreConfigServiceBase(config)
|
||||
{
|
||||
private Config _config;
|
||||
private static readonly string _tag = "CoreConfigSingboxService";
|
||||
|
||||
public CoreConfigSingboxService(Config config)
|
||||
{
|
||||
_config = config;
|
||||
}
|
||||
|
||||
#region public gen function
|
||||
|
||||
public async Task<RetResult> GenerateClientConfigContent(ProfileItem node)
|
||||
public override async Task<RetResult> GenerateClientConfigContent(ProfileItem node)
|
||||
{
|
||||
var ret = new RetResult();
|
||||
try
|
||||
|
|
@ -92,7 +86,7 @@ public class CoreConfigSingboxService
|
|||
}
|
||||
}
|
||||
|
||||
public async Task<RetResult> GenerateClientSpeedtestConfig(List<ServerTestItem> selecteds)
|
||||
public override async Task<RetResult> GenerateClientSpeedtestConfig(List<ServerTestItem> selecteds)
|
||||
{
|
||||
var ret = new RetResult();
|
||||
try
|
||||
|
|
@ -141,7 +135,7 @@ public class CoreConfigSingboxService
|
|||
|
||||
foreach (var it in selecteds)
|
||||
{
|
||||
if (it.ConfigType == EConfigType.Custom)
|
||||
if (!Global.SingboxSupportConfigType.Contains(it.ConfigType))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
@ -269,7 +263,7 @@ public class CoreConfigSingboxService
|
|||
}
|
||||
}
|
||||
|
||||
public async Task<RetResult> GenerateClientSpeedtestConfig(ProfileItem node, int port)
|
||||
public override async Task<RetResult> GenerateClientSpeedtestConfig(ProfileItem node, int port)
|
||||
{
|
||||
var ret = new RetResult();
|
||||
try
|
||||
|
|
@ -340,7 +334,7 @@ public class CoreConfigSingboxService
|
|||
}
|
||||
}
|
||||
|
||||
public async Task<RetResult> GenerateClientMultipleLoadConfig(List<ProfileItem> selecteds)
|
||||
public override async Task<RetResult> GenerateClientMultipleLoadConfig(List<ProfileItem> selecteds, EMultipleLoad multipleLoad)
|
||||
{
|
||||
var ret = new RetResult();
|
||||
try
|
||||
|
|
@ -377,7 +371,7 @@ public class CoreConfigSingboxService
|
|||
var proxyProfiles = new List<ProfileItem>();
|
||||
foreach (var it in selecteds)
|
||||
{
|
||||
if (it.ConfigType == EConfigType.Custom)
|
||||
if (!Global.SingboxSupportConfigType.Contains(it.ConfigType))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
@ -432,7 +426,7 @@ public class CoreConfigSingboxService
|
|||
}
|
||||
}
|
||||
|
||||
public async Task<RetResult> GenerateClientCustomConfig(ProfileItem node, string? fileName)
|
||||
public override async Task<RetResult> GenerateClientCustomConfig(ProfileItem node, string? fileName)
|
||||
{
|
||||
var ret = new RetResult();
|
||||
if (node == null || fileName is null)
|
||||
|
|
@ -513,6 +507,92 @@ public class CoreConfigSingboxService
|
|||
}
|
||||
}
|
||||
|
||||
protected override async Task<RetResult> GeneratePassthroughConfig(ProfileItem node, int port)
|
||||
{
|
||||
var ret = new RetResult();
|
||||
try
|
||||
{
|
||||
if (node == null
|
||||
|| node.Port <= 0)
|
||||
{
|
||||
ret.Msg = ResUI.CheckServerSettings;
|
||||
return ret;
|
||||
}
|
||||
if (node.GetNetwork() is nameof(ETransport.kcp) or nameof(ETransport.xhttp))
|
||||
{
|
||||
ret.Msg = ResUI.Incorrectconfiguration + $" - {node.GetNetwork()}";
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret.Msg = ResUI.InitialConfiguration;
|
||||
|
||||
var result = EmbedUtils.GetEmbedText(Global.SingboxSampleClient);
|
||||
if (result.IsNullOrEmpty())
|
||||
{
|
||||
ret.Msg = ResUI.FailedGetDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
|
||||
var singboxConfig = JsonUtils.Deserialize<SingboxConfig>(result);
|
||||
if (singboxConfig == null)
|
||||
{
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
|
||||
await GenLog(singboxConfig);
|
||||
|
||||
var inbound = new Inbound4Sbox()
|
||||
{
|
||||
type = EInboundProtocol.mixed.ToString(),
|
||||
tag = EInboundProtocol.socks.ToString(),
|
||||
listen = Global.Loopback,
|
||||
listen_port = port
|
||||
};
|
||||
singboxConfig.inbounds = new() { inbound };
|
||||
|
||||
if (node.ConfigType == EConfigType.WireGuard)
|
||||
{
|
||||
singboxConfig.outbounds.RemoveAt(0);
|
||||
var endpoints = new Endpoints4Sbox();
|
||||
await GenEndpoint(node, endpoints);
|
||||
endpoints.tag = Global.ProxyTag;
|
||||
singboxConfig.endpoints = new() { endpoints };
|
||||
}
|
||||
else
|
||||
{
|
||||
await GenOutbound(node, singboxConfig.outbounds.First());
|
||||
}
|
||||
|
||||
if (singboxConfig.endpoints == null)
|
||||
{
|
||||
singboxConfig.outbounds = new() { JsonUtils.DeepCopy(singboxConfig.outbounds.First()) };
|
||||
}
|
||||
else
|
||||
{
|
||||
singboxConfig.outbounds.Clear();
|
||||
}
|
||||
|
||||
await GenMoreOutbounds(node, singboxConfig);
|
||||
|
||||
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
|
||||
ret.Success = true;
|
||||
var config = JsonNode.Parse(JsonUtils.Serialize(singboxConfig)).AsObject();
|
||||
|
||||
config.Remove("route");
|
||||
|
||||
ret.Data = JsonUtils.Serialize(config, true);
|
||||
|
||||
return ret;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion public gen function
|
||||
|
||||
#region private gen function
|
||||
|
|
@ -1030,7 +1110,7 @@ public class CoreConfigSingboxService
|
|||
var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
||||
string? prevOutboundTag = null;
|
||||
if (prevNode is not null
|
||||
&& prevNode.ConfigType != EConfigType.Custom)
|
||||
&& Global.SingboxSupportConfigType.Contains(prevNode.ConfigType))
|
||||
{
|
||||
prevOutboundTag = $"prev-{Global.ProxyTag}";
|
||||
var prevServer = await GenServer(prevNode);
|
||||
|
|
@ -1121,7 +1201,7 @@ public class CoreConfigSingboxService
|
|||
{
|
||||
var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
||||
if (prevNode is not null
|
||||
&& prevNode.ConfigType != EConfigType.Custom)
|
||||
&& Global.SingboxSupportConfigType.Contains(prevNode.ConfigType))
|
||||
{
|
||||
var prevOutbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
|
||||
await GenOutbound(prevNode, prevOutbound);
|
||||
|
|
@ -1228,7 +1308,7 @@ public class CoreConfigSingboxService
|
|||
// Next proxy
|
||||
var nextNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.NextProfile);
|
||||
if (nextNode is not null
|
||||
&& nextNode.ConfigType != EConfigType.Custom)
|
||||
&& Global.SingboxSupportConfigType.Contains(nextNode.ConfigType))
|
||||
{
|
||||
nextOutbound ??= await GenServer(nextNode);
|
||||
nextOutbound.tag = outbound.tag;
|
||||
|
|
@ -1565,7 +1645,7 @@ public class CoreConfigSingboxService
|
|||
|
||||
var node = await AppHandler.Instance.GetProfileItemViaRemarks(outboundTag);
|
||||
if (node == null
|
||||
|| node.ConfigType == EConfigType.Custom)
|
||||
|| !Global.SingboxSupportConfigType.Contains(node.ConfigType))
|
||||
{
|
||||
return Global.ProxyTag;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,15 @@
|
|||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace ServiceLib.Services.CoreConfig;
|
||||
|
||||
public class CoreConfigV2rayService
|
||||
public class CoreConfigV2rayService(Config config) : CoreConfigServiceBase(config)
|
||||
{
|
||||
private Config _config;
|
||||
private static readonly string _tag = "CoreConfigV2rayService";
|
||||
|
||||
public CoreConfigV2rayService(Config config)
|
||||
{
|
||||
_config = config;
|
||||
}
|
||||
|
||||
#region public gen function
|
||||
|
||||
public async Task<RetResult> GenerateClientConfigContent(ProfileItem node)
|
||||
public override async Task<RetResult> GenerateClientConfigContent(ProfileItem node)
|
||||
{
|
||||
var ret = new RetResult();
|
||||
try
|
||||
|
|
@ -77,7 +70,7 @@ public class CoreConfigV2rayService
|
|||
}
|
||||
}
|
||||
|
||||
public async Task<RetResult> GenerateClientMultipleLoadConfig(List<ProfileItem> selecteds, EMultipleLoad multipleLoad)
|
||||
public override async Task<RetResult> GenerateClientMultipleLoadConfig(List<ProfileItem> selecteds, EMultipleLoad multipleLoad)
|
||||
{
|
||||
var ret = new RetResult();
|
||||
|
||||
|
|
@ -116,11 +109,7 @@ public class CoreConfigV2rayService
|
|||
var proxyProfiles = new List<ProfileItem>();
|
||||
foreach (var it in selecteds)
|
||||
{
|
||||
if (it.ConfigType == EConfigType.Custom)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (it.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls)
|
||||
if (!Global.XraySupportConfigType.Contains(it.ConfigType))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
@ -206,7 +195,7 @@ public class CoreConfigV2rayService
|
|||
}
|
||||
}
|
||||
|
||||
public async Task<RetResult> GenerateClientSpeedtestConfig(List<ServerTestItem> selecteds)
|
||||
public override async Task<RetResult> GenerateClientSpeedtestConfig(List<ServerTestItem> selecteds)
|
||||
{
|
||||
var ret = new RetResult();
|
||||
try
|
||||
|
|
@ -255,7 +244,7 @@ public class CoreConfigV2rayService
|
|||
|
||||
foreach (var it in selecteds)
|
||||
{
|
||||
if (it.ConfigType == EConfigType.Custom)
|
||||
if (!Global.SingboxSupportConfigType.Contains(it.ConfigType))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
@ -358,7 +347,7 @@ public class CoreConfigV2rayService
|
|||
}
|
||||
}
|
||||
|
||||
public async Task<RetResult> GenerateClientSpeedtestConfig(ProfileItem node, int port)
|
||||
public override async Task<RetResult> GenerateClientSpeedtestConfig(ProfileItem node, int port)
|
||||
{
|
||||
var ret = new RetResult();
|
||||
try
|
||||
|
|
@ -416,6 +405,80 @@ public class CoreConfigV2rayService
|
|||
}
|
||||
}
|
||||
|
||||
protected override async Task<RetResult> GeneratePassthroughConfig(ProfileItem node, int port)
|
||||
{
|
||||
var ret = new RetResult();
|
||||
try
|
||||
{
|
||||
if (node == null
|
||||
|| node.Port <= 0)
|
||||
{
|
||||
ret.Msg = ResUI.CheckServerSettings;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (node.GetNetwork() is nameof(ETransport.quic))
|
||||
{
|
||||
ret.Msg = ResUI.Incorrectconfiguration + $" - {node.GetNetwork()}";
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret.Msg = ResUI.InitialConfiguration;
|
||||
|
||||
var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
|
||||
if (result.IsNullOrEmpty())
|
||||
{
|
||||
ret.Msg = ResUI.FailedGetDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
|
||||
var v2rayConfig = JsonUtils.Deserialize<V2rayConfig>(result);
|
||||
if (v2rayConfig == null)
|
||||
{
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
|
||||
await GenLog(v2rayConfig);
|
||||
|
||||
v2rayConfig.inbounds = new() { new()
|
||||
{
|
||||
tag = EInboundProtocol.socks.ToString(),
|
||||
listen = Global.Loopback,
|
||||
port = port,
|
||||
protocol = EInboundProtocol.mixed.ToString(),
|
||||
settings = new Inboundsettings4Ray()
|
||||
{
|
||||
udp = true,
|
||||
auth = "noauth"
|
||||
},
|
||||
} };
|
||||
|
||||
await GenOutbound(node, v2rayConfig.outbounds.First());
|
||||
|
||||
v2rayConfig.outbounds = new() { JsonUtils.DeepCopy(v2rayConfig.outbounds.First()) };
|
||||
|
||||
await GenMoreOutbounds(node, v2rayConfig);
|
||||
|
||||
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
|
||||
ret.Success = true;
|
||||
|
||||
var config = JsonNode.Parse(JsonUtils.Serialize(v2rayConfig)).AsObject();
|
||||
|
||||
config.Remove("routing");
|
||||
|
||||
ret.Data = JsonUtils.Serialize(config, true);
|
||||
|
||||
return ret;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion public gen function
|
||||
|
||||
#region private gen function
|
||||
|
|
@ -638,10 +701,7 @@ public class CoreConfigV2rayService
|
|||
|
||||
var node = await AppHandler.Instance.GetProfileItemViaRemarks(outboundTag);
|
||||
if (node == null
|
||||
|| node.ConfigType == EConfigType.Custom
|
||||
|| node.ConfigType == EConfigType.Hysteria2
|
||||
|| node.ConfigType == EConfigType.TUIC
|
||||
|| node.ConfigType == EConfigType.Anytls)
|
||||
|| !Global.SingboxSupportConfigType.Contains(node.ConfigType))
|
||||
{
|
||||
return Global.ProxyTag;
|
||||
}
|
||||
|
|
@ -1220,10 +1280,7 @@ public class CoreConfigV2rayService
|
|||
// Previous proxy
|
||||
var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
||||
if (prevNode is not null
|
||||
&& prevNode.ConfigType != EConfigType.Custom
|
||||
&& prevNode.ConfigType != EConfigType.Hysteria2
|
||||
&& prevNode.ConfigType != EConfigType.TUIC
|
||||
&& prevNode.ConfigType != EConfigType.Anytls
|
||||
&& Global.SingboxSupportConfigType.Contains(prevNode.ConfigType)
|
||||
&& Utils.IsDomain(prevNode.Address))
|
||||
{
|
||||
domainList.Add(prevNode.Address);
|
||||
|
|
@ -1232,10 +1289,7 @@ public class CoreConfigV2rayService
|
|||
// Next proxy
|
||||
var nextNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.NextProfile);
|
||||
if (nextNode is not null
|
||||
&& nextNode.ConfigType != EConfigType.Custom
|
||||
&& nextNode.ConfigType != EConfigType.Hysteria2
|
||||
&& nextNode.ConfigType != EConfigType.TUIC
|
||||
&& nextNode.ConfigType != EConfigType.Anytls
|
||||
&& Global.SingboxSupportConfigType.Contains(nextNode.ConfigType)
|
||||
&& Utils.IsDomain(nextNode.Address))
|
||||
{
|
||||
domainList.Add(nextNode.Address);
|
||||
|
|
@ -1351,10 +1405,7 @@ public class CoreConfigV2rayService
|
|||
var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
||||
string? prevOutboundTag = null;
|
||||
if (prevNode is not null
|
||||
&& prevNode.ConfigType != EConfigType.Custom
|
||||
&& prevNode.ConfigType != EConfigType.Hysteria2
|
||||
&& prevNode.ConfigType != EConfigType.TUIC
|
||||
&& prevNode.ConfigType != EConfigType.Anytls)
|
||||
&& Global.XraySupportConfigType.Contains(prevNode.ConfigType))
|
||||
{
|
||||
var prevOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||
await GenOutbound(prevNode, prevOutbound);
|
||||
|
|
@ -1427,10 +1478,7 @@ public class CoreConfigV2rayService
|
|||
{
|
||||
var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
||||
if (prevNode is not null
|
||||
&& prevNode.ConfigType != EConfigType.Custom
|
||||
&& prevNode.ConfigType != EConfigType.Hysteria2
|
||||
&& prevNode.ConfigType != EConfigType.TUIC
|
||||
&& prevNode.ConfigType != EConfigType.Anytls)
|
||||
&& !Global.XraySupportConfigType.Contains(prevNode.ConfigType))
|
||||
{
|
||||
var prevOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||
await GenOutbound(prevNode, prevOutbound);
|
||||
|
|
@ -1497,10 +1545,7 @@ public class CoreConfigV2rayService
|
|||
// Next proxy
|
||||
var nextNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.NextProfile);
|
||||
if (nextNode is not null
|
||||
&& nextNode.ConfigType != EConfigType.Custom
|
||||
&& nextNode.ConfigType != EConfigType.Hysteria2
|
||||
&& nextNode.ConfigType != EConfigType.TUIC
|
||||
&& nextNode.ConfigType != EConfigType.Anytls)
|
||||
&& !Global.XraySupportConfigType.Contains(nextNode.ConfigType))
|
||||
{
|
||||
if (nextOutbound == null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
namespace ServiceLib.Services.CoreConfig.Minimal;
|
||||
public class CoreConfigBrookService(Config config) : CoreConfigServiceMinimalBase(config)
|
||||
{
|
||||
protected override async Task<RetResult> GeneratePassthroughConfig(ProfileItem node, int port)
|
||||
{
|
||||
var ret = new RetResult();
|
||||
try
|
||||
{
|
||||
if (node == null
|
||||
|| node.Port <= 0)
|
||||
{
|
||||
ret.Msg = ResUI.CheckServerSettings;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (node.ConfigType != EConfigType.Brook)
|
||||
{
|
||||
ret.Msg = ResUI.Incorrectconfiguration + $" - {node.ConfigType}";
|
||||
return ret;
|
||||
}
|
||||
|
||||
var processArgs = "client";
|
||||
|
||||
// inbound
|
||||
processArgs += " --socks5 " + Global.Loopback + ":" + port.ToString();
|
||||
|
||||
// outbound
|
||||
processArgs += " --server " + node.Address + ":" + node.Port;
|
||||
processArgs += " --password " + node.Id;
|
||||
|
||||
ret.Success = true;
|
||||
ret.Data = processArgs;
|
||||
|
||||
return await Task.FromResult(ret);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return await Task.FromResult(ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +1,21 @@
|
|||
namespace ServiceLib.Services.CoreConfig;
|
||||
namespace ServiceLib.Services.CoreConfig.Minimal;
|
||||
|
||||
/// <summary>
|
||||
/// Core configuration file processing class
|
||||
/// </summary>
|
||||
public class CoreConfigClashService
|
||||
public class CoreConfigClashService(Config config) : CoreConfigServiceMinimalBase(config)
|
||||
{
|
||||
private Config _config;
|
||||
private static readonly string _tag = "CoreConfigClashService";
|
||||
|
||||
public CoreConfigClashService(Config config)
|
||||
protected override async Task<RetResult> GeneratePassthroughConfig(ProfileItem node, int port)
|
||||
{
|
||||
_config = config;
|
||||
var ret = new RetResult
|
||||
{
|
||||
Success = false,
|
||||
Msg = ResUI.OperationFailed
|
||||
};
|
||||
return await Task.FromResult(ret);
|
||||
}
|
||||
|
||||
public async Task<RetResult> GenerateClientCustomConfig(ProfileItem node, string? fileName)
|
||||
public override async Task<RetResult> GenerateClientCustomConfig(ProfileItem node, string? fileName)
|
||||
{
|
||||
var ret = new RetResult();
|
||||
if (node == null || fileName is null)
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace ServiceLib.Services.CoreConfig.Minimal;
|
||||
public class CoreConfigHy2Service(Config config) : CoreConfigServiceMinimalBase(config)
|
||||
{
|
||||
protected override async Task<RetResult> GeneratePassthroughConfig(ProfileItem node, int port)
|
||||
{
|
||||
var ret = new RetResult();
|
||||
try
|
||||
{
|
||||
if (node == null
|
||||
|| node.Port <= 0)
|
||||
{
|
||||
ret.Msg = ResUI.CheckServerSettings;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (node.ConfigType != EConfigType.Hysteria2)
|
||||
{
|
||||
ret.Msg = ResUI.Incorrectconfiguration + $" - {node.ConfigType}";
|
||||
return ret;
|
||||
}
|
||||
|
||||
var configJsonNode = new JsonObject();
|
||||
|
||||
// inbound
|
||||
configJsonNode["socks5"] = new JsonObject
|
||||
{
|
||||
["listen"] = Global.Loopback + ":" + port.ToString()
|
||||
};
|
||||
|
||||
// outbound
|
||||
var outboundPort = string.Empty;
|
||||
if (node.Ports.IsNotEmpty())
|
||||
{
|
||||
outboundPort = node.Ports.Replace(':', '-');
|
||||
if (_config.HysteriaItem.HopInterval > 0)
|
||||
{
|
||||
configJsonNode["transport"] = new JsonObject
|
||||
{
|
||||
["udp"] = new JsonObject
|
||||
{
|
||||
["hopInterval"] = $"{_config.HysteriaItem.HopInterval}s"
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
outboundPort = node.Port.ToString();
|
||||
}
|
||||
configJsonNode["server"] = node.Address + ":" + outboundPort;
|
||||
configJsonNode["auth"] = node.Id;
|
||||
|
||||
if (node.Sni.IsNotEmpty())
|
||||
{
|
||||
configJsonNode["tls"] = new JsonObject
|
||||
{
|
||||
["sni"] = node.Sni,
|
||||
["insecure"] = node.AllowInsecure.ToLower() == "true"
|
||||
};
|
||||
}
|
||||
|
||||
if (node.Path.IsNotEmpty())
|
||||
{
|
||||
configJsonNode["obfs"] = new JsonObject
|
||||
{
|
||||
["type"] = "salamander ",
|
||||
["salamander"] = new JsonObject
|
||||
{
|
||||
["password"] = node.Path
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var bandwidthObject = new JsonObject();
|
||||
if (_config.HysteriaItem.UpMbps > 0)
|
||||
{
|
||||
bandwidthObject["up"] = $"{_config.HysteriaItem.UpMbps} mbps";
|
||||
}
|
||||
if (_config.HysteriaItem.DownMbps > 0)
|
||||
{
|
||||
bandwidthObject["down"] = $"{_config.HysteriaItem.DownMbps} mbps";
|
||||
}
|
||||
if (bandwidthObject.Count > 0)
|
||||
{
|
||||
configJsonNode["bandwidth"] = bandwidthObject;
|
||||
}
|
||||
|
||||
ret.Success = true;
|
||||
ret.Data = JsonUtils.Serialize(configJsonNode, true);
|
||||
|
||||
return await Task.FromResult(ret);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return await Task.FromResult(ret);
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task<RetResult> GenerateClientCustomConfig(ProfileItem node, string? fileName)
|
||||
{
|
||||
var ret = new RetResult();
|
||||
try
|
||||
{
|
||||
if (node == null || fileName is null)
|
||||
{
|
||||
ret.Msg = ResUI.CheckServerSettings;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (File.Exists(fileName))
|
||||
{
|
||||
File.SetAttributes(fileName, FileAttributes.Normal); //If the file has a read-only attribute, direct deletion will fail
|
||||
File.Delete(fileName);
|
||||
}
|
||||
|
||||
string addressFileName = node.Address;
|
||||
if (!File.Exists(addressFileName))
|
||||
{
|
||||
addressFileName = Utils.GetConfigPath(addressFileName);
|
||||
}
|
||||
if (!File.Exists(addressFileName))
|
||||
{
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
// Try deserializing the file to check if it is a valid JSON or YAML file
|
||||
var fileContent = File.ReadAllText(addressFileName);
|
||||
var jsonContent = JsonUtils.Deserialize<JsonObject>(fileContent);
|
||||
if (jsonContent != null)
|
||||
{
|
||||
File.Copy(addressFileName, fileName);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If it's YAML, convert to JSON and write it
|
||||
var yamlContent = YamlUtils.FromYaml<Dictionary<string, object>>(fileContent);
|
||||
if (yamlContent != null)
|
||||
{
|
||||
File.WriteAllText(fileName, JsonUtils.Serialize(yamlContent, true));
|
||||
}
|
||||
else
|
||||
{
|
||||
ret.Msg = ResUI.FailedReadConfiguration + "2";
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
File.SetAttributes(fileName, FileAttributes.Normal); //Copy will keep the attributes of addressFileName, so we need to add write permissions to fileName just in case of addressFileName is a read-only file.
|
||||
|
||||
//check again
|
||||
if (!File.Exists(fileName))
|
||||
{
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
|
||||
ret.Success = true;
|
||||
return await Task.FromResult(ret);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace ServiceLib.Services.CoreConfig.Minimal;
|
||||
public class CoreConfigJuicityService(Config config) : CoreConfigServiceMinimalBase(config)
|
||||
{
|
||||
protected override async Task<RetResult> GeneratePassthroughConfig(ProfileItem node, int port)
|
||||
{
|
||||
var ret = new RetResult();
|
||||
try
|
||||
{
|
||||
if (node == null
|
||||
|| node.Port <= 0)
|
||||
{
|
||||
ret.Msg = ResUI.CheckServerSettings;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (node.ConfigType != EConfigType.Juicity)
|
||||
{
|
||||
ret.Msg = ResUI.Incorrectconfiguration + $" - {node.ConfigType}";
|
||||
return ret;
|
||||
}
|
||||
|
||||
var configJsonNode = new JsonObject();
|
||||
|
||||
// log
|
||||
var logLevel = string.Empty;
|
||||
switch (_config.CoreBasicItem.Loglevel)
|
||||
{
|
||||
case "warning":
|
||||
logLevel = "warn";
|
||||
break;
|
||||
default:
|
||||
logLevel = _config.CoreBasicItem.Loglevel;
|
||||
break;
|
||||
}
|
||||
configJsonNode["log_level"] = logLevel;
|
||||
|
||||
// inbound
|
||||
configJsonNode["listen"] = ":" + port.ToString();
|
||||
|
||||
// outbound
|
||||
configJsonNode["server"] = node.Address + ":" + node.Port;
|
||||
configJsonNode["uuid"] = node.Id;
|
||||
configJsonNode["password"] = node.Security;
|
||||
if (node.Sni.IsNotEmpty())
|
||||
{
|
||||
configJsonNode["sni"] = node.Sni;
|
||||
}
|
||||
configJsonNode["allow_insecure"] = node.AllowInsecure == "true";
|
||||
configJsonNode["congestion_control"] = node.HeaderType;
|
||||
|
||||
ret.Success = true;
|
||||
ret.Data = JsonUtils.Serialize(configJsonNode, true);
|
||||
|
||||
return await Task.FromResult(ret);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return await Task.FromResult(ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace ServiceLib.Services.CoreConfig.Minimal;
|
||||
public class CoreConfigNaiveService(Config config) : CoreConfigServiceMinimalBase(config)
|
||||
{
|
||||
protected override async Task<RetResult> GeneratePassthroughConfig(ProfileItem node, int port)
|
||||
{
|
||||
var ret = new RetResult();
|
||||
try
|
||||
{
|
||||
if (node == null
|
||||
|| node.Port <= 0)
|
||||
{
|
||||
ret.Msg = ResUI.CheckServerSettings;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (node.ConfigType != EConfigType.NaiveProxy)
|
||||
{
|
||||
ret.Msg = ResUI.Incorrectconfiguration + $" - {node.ConfigType}";
|
||||
return ret;
|
||||
}
|
||||
|
||||
var configJsonNode = new JsonObject();
|
||||
|
||||
// inbound
|
||||
configJsonNode["listen"] = Global.SocksProtocol + Global.Loopback + ":" + port.ToString();
|
||||
|
||||
// outbound
|
||||
configJsonNode["proxy"] = (node.HeaderType == "quic" ? "quic://" : Global.HttpsProtocol) + node.Id + "@" + node.Address + ":" + node.Port;
|
||||
|
||||
ret.Success = true;
|
||||
ret.Data = JsonUtils.Serialize(configJsonNode, true);
|
||||
|
||||
return await Task.FromResult(ret);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return await Task.FromResult(ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace ServiceLib.Services.CoreConfig.Minimal;
|
||||
public class CoreConfigShadowquicService(Config config) : CoreConfigServiceMinimalBase(config)
|
||||
{
|
||||
protected override async Task<RetResult> GeneratePassthroughConfig(ProfileItem node, int port)
|
||||
{
|
||||
var ret = new RetResult();
|
||||
try
|
||||
{
|
||||
if (node == null
|
||||
|| node.Port <= 0)
|
||||
{
|
||||
ret.Msg = ResUI.CheckServerSettings;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (node.ConfigType != EConfigType.Shadowquic)
|
||||
{
|
||||
ret.Msg = ResUI.Incorrectconfiguration + $" - {node.ConfigType}";
|
||||
return ret;
|
||||
}
|
||||
|
||||
var configYamlNode = new Dictionary<string, object>();
|
||||
|
||||
// log
|
||||
var logLevel = string.Empty;
|
||||
switch (_config.CoreBasicItem.Loglevel)
|
||||
{
|
||||
case "warning":
|
||||
logLevel = "warn";
|
||||
break;
|
||||
default:
|
||||
logLevel = _config.CoreBasicItem.Loglevel;
|
||||
break;
|
||||
}
|
||||
configYamlNode["log-level"] = logLevel;
|
||||
|
||||
// inbound
|
||||
var inboundNode = new Dictionary<string, object>
|
||||
{
|
||||
["type"] = "socks5",
|
||||
["listen"] = Global.Loopback + ":" + port.ToString()
|
||||
};
|
||||
configYamlNode["inbound"] = inboundNode;
|
||||
|
||||
// outbound
|
||||
var alpn = new List<string>();
|
||||
foreach (var item in node.GetAlpn() ?? new List<string>())
|
||||
{
|
||||
alpn.Add(item);
|
||||
}
|
||||
if (alpn.Count == 0)
|
||||
{
|
||||
alpn.Add("h3");
|
||||
}
|
||||
|
||||
var outboundNode = new Dictionary<string, object>
|
||||
{
|
||||
["type"] = "shadowquic",
|
||||
["addr"] = node.Address + ":" + node.Port,
|
||||
["password"] = node.Id,
|
||||
["username"] = node.Security,
|
||||
["alpn"] = alpn,
|
||||
["congestion-control"] = node.HeaderType,
|
||||
["zero-rtt"] = true
|
||||
};
|
||||
if (node.Sni.IsNotEmpty())
|
||||
{
|
||||
outboundNode["server-name"] = node.Sni;
|
||||
}
|
||||
configYamlNode["outbound"] = outboundNode;
|
||||
|
||||
ret.Success = true;
|
||||
ret.Data = YamlUtils.ToYaml(configYamlNode);
|
||||
|
||||
return await Task.FromResult(ret);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return await Task.FromResult(ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace ServiceLib.Services.CoreConfig.Minimal;
|
||||
public class CoreConfigTuicService(Config config) : CoreConfigServiceMinimalBase(config)
|
||||
{
|
||||
protected override async Task<RetResult> GeneratePassthroughConfig(ProfileItem node, int port)
|
||||
{
|
||||
var ret = new RetResult();
|
||||
try
|
||||
{
|
||||
if (node == null
|
||||
|| node.Port <= 0)
|
||||
{
|
||||
ret.Msg = ResUI.CheckServerSettings;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (node.ConfigType != EConfigType.TUIC)
|
||||
{
|
||||
ret.Msg = ResUI.Incorrectconfiguration + $" - {node.ConfigType}";
|
||||
return ret;
|
||||
}
|
||||
|
||||
var configJsonNode = new JsonObject();
|
||||
|
||||
// log
|
||||
var logLevel = string.Empty;
|
||||
switch (_config.CoreBasicItem.Loglevel)
|
||||
{
|
||||
case "warning":
|
||||
logLevel = "warn";
|
||||
break;
|
||||
default:
|
||||
logLevel = _config.CoreBasicItem.Loglevel;
|
||||
break;
|
||||
}
|
||||
configJsonNode["log_level"] = logLevel;
|
||||
|
||||
// inbound
|
||||
configJsonNode["local"] = new JsonObject
|
||||
{
|
||||
["server"] = Global.Loopback + ":" + port.ToString()
|
||||
};
|
||||
|
||||
// outbound
|
||||
var alpn = new JsonArray();
|
||||
foreach(var item in node.GetAlpn() ?? new List<string>())
|
||||
{
|
||||
alpn.Add(item);
|
||||
}
|
||||
if (alpn.Count == 0)
|
||||
{
|
||||
alpn.Add("h3");
|
||||
}
|
||||
|
||||
configJsonNode["relay"] = new JsonObject
|
||||
{
|
||||
["server"] = node.Address + ":" + node.Port,
|
||||
["uuid"] = node.Id,
|
||||
["password"] = node.Security,
|
||||
["udp_relay_mode"] = "quic",
|
||||
["congestion_control"] = node.HeaderType,
|
||||
["alpn"] = alpn
|
||||
};
|
||||
|
||||
ret.Success = true;
|
||||
ret.Data = JsonUtils.Serialize(configJsonNode, true);
|
||||
|
||||
return await Task.FromResult(ret);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return await Task.FromResult(ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
|
|
@ -70,7 +71,7 @@ public class SpeedtestService
|
|||
var lstSelected = new List<ServerTestItem>();
|
||||
foreach (var it in selecteds)
|
||||
{
|
||||
if (it.ConfigType == EConfigType.Custom)
|
||||
if (!(Global.XraySupportConfigType.Contains(it.ConfigType) || Global.SingboxSupportConfigType.Contains(it.ConfigType)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
@ -122,7 +123,7 @@ public class SpeedtestService
|
|||
List<Task> tasks = [];
|
||||
foreach (var it in selecteds)
|
||||
{
|
||||
if (it.ConfigType == EConfigType.Custom)
|
||||
if (!(Global.XraySupportConfigType.Contains(it.ConfigType) || Global.SingboxSupportConfigType.Contains(it.ConfigType)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
@ -207,7 +208,7 @@ public class SpeedtestService
|
|||
{
|
||||
continue;
|
||||
}
|
||||
if (it.ConfigType == EConfigType.Custom)
|
||||
if (!(Global.XraySupportConfigType.Contains(it.ConfigType) || Global.SingboxSupportConfigType.Contains(it.ConfigType)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
@ -244,7 +245,7 @@ public class SpeedtestService
|
|||
UpdateFunc(it.IndexId, "", ResUI.SpeedtestingSkip);
|
||||
continue;
|
||||
}
|
||||
if (it.ConfigType == EConfigType.Custom)
|
||||
if (!(Global.XraySupportConfigType.Contains(it.ConfigType) || Global.SingboxSupportConfigType.Contains(it.ConfigType)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
@ -358,8 +359,8 @@ public class SpeedtestService
|
|||
private List<List<ServerTestItem>> GetTestBatchItem(List<ServerTestItem> lstSelected, int pageSize)
|
||||
{
|
||||
List<List<ServerTestItem>> lstTest = new();
|
||||
var lst1 = lstSelected.Where(t => t.ConfigType is not (EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls)).ToList();
|
||||
var lst2 = lstSelected.Where(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls).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();
|
||||
|
||||
for (var num = 0; num < (int)Math.Ceiling(lst1.Count * 1.0 / pageSize); num++)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -25,6 +25,10 @@ public class MainWindowViewModel : MyReactiveObject
|
|||
public ReactiveCommand<Unit, Unit> AddServerViaClipboardCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> AddServerViaScanCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> AddServerViaImageCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> AddBrookServerCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> AddJuicityServerCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> AddNaiveServerCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> AddShadowquicServerCmd { get; }
|
||||
|
||||
//Subscription
|
||||
public ReactiveCommand<Unit, Unit> SubSettingCmd { get; }
|
||||
|
|
@ -116,6 +120,22 @@ public class MainWindowViewModel : MyReactiveObject
|
|||
{
|
||||
await AddServerAsync(true, EConfigType.Anytls);
|
||||
});
|
||||
AddBrookServerCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await AddServerAsync(true, EConfigType.Brook);
|
||||
});
|
||||
AddJuicityServerCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await AddServerAsync(true, EConfigType.Juicity);
|
||||
});
|
||||
AddNaiveServerCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await AddServerAsync(true, EConfigType.NaiveProxy);
|
||||
});
|
||||
AddShadowquicServerCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await AddServerAsync(true, EConfigType.Shadowquic);
|
||||
});
|
||||
AddCustomServerCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await AddServerAsync(true, EConfigType.Custom);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System.Reactive;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using ServiceLib.Models;
|
||||
|
||||
namespace ServiceLib.ViewModels;
|
||||
|
||||
|
|
@ -104,6 +105,21 @@ public class OptionSettingViewModel : MyReactiveObject
|
|||
|
||||
#endregion CoreType
|
||||
|
||||
#region SplitCoreType
|
||||
|
||||
[Reactive] public bool EnableSplitCore { get; set; }
|
||||
[Reactive] public string SplitCoreType1 { get; set; }
|
||||
[Reactive] public string SplitCoreType3 { get; set; }
|
||||
[Reactive] public string SplitCoreType4 { get; set; }
|
||||
[Reactive] public string SplitCoreType5 { get; set; }
|
||||
[Reactive] public string SplitCoreType6 { get; set; }
|
||||
[Reactive] public string SplitCoreType7 { get; set; }
|
||||
[Reactive] public string SplitCoreType8 { get; set; }
|
||||
[Reactive] public string SplitCoreType9 { get; set; }
|
||||
[Reactive] public string RouteSplitCoreType { get; set; }
|
||||
|
||||
#endregion SplitCoreType
|
||||
|
||||
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
||||
|
||||
public OptionSettingViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
|
|
@ -210,14 +226,13 @@ public class OptionSettingViewModel : MyReactiveObject
|
|||
#endregion Tun mode
|
||||
|
||||
await InitCoreType();
|
||||
|
||||
await InitSplitCoreItem();
|
||||
}
|
||||
|
||||
private async Task InitCoreType()
|
||||
{
|
||||
if (_config.CoreTypeItem == null)
|
||||
{
|
||||
_config.CoreTypeItem = new List<CoreTypeItem>();
|
||||
}
|
||||
_config.CoreTypeItem ??= new List<CoreTypeItem>();
|
||||
|
||||
foreach (EConfigType it in Enum.GetValues(typeof(EConfigType)))
|
||||
{
|
||||
|
|
@ -232,6 +247,7 @@ public class OptionSettingViewModel : MyReactiveObject
|
|||
CoreType = ECoreType.Xray
|
||||
});
|
||||
}
|
||||
|
||||
_config.CoreTypeItem.ForEach(it =>
|
||||
{
|
||||
var type = it.CoreType.ToString();
|
||||
|
|
@ -269,6 +285,87 @@ public class OptionSettingViewModel : MyReactiveObject
|
|||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task InitSplitCoreItem()
|
||||
{
|
||||
_config.SplitCoreItem ??= new()
|
||||
{
|
||||
EnableSplitCore = false,
|
||||
SplitCoreTypes = new List<CoreTypeItem>(),
|
||||
RouteCoreType = ECoreType.Xray
|
||||
};
|
||||
|
||||
foreach (EConfigType it in Enum.GetValues(typeof(EConfigType)))
|
||||
{
|
||||
if (_config.SplitCoreItem.SplitCoreTypes.FindIndex(t => t.ConfigType == it) >= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (it is EConfigType.Hysteria2 or EConfigType.TUIC)
|
||||
{
|
||||
_config.SplitCoreItem.SplitCoreTypes.Add(new CoreTypeItem()
|
||||
{
|
||||
ConfigType = it,
|
||||
CoreType = ECoreType.sing_box
|
||||
});
|
||||
continue;
|
||||
}
|
||||
if (it is EConfigType.Custom)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_config.SplitCoreItem.SplitCoreTypes.Add(new CoreTypeItem()
|
||||
{
|
||||
ConfigType = it,
|
||||
CoreType = ECoreType.Xray
|
||||
});
|
||||
}
|
||||
|
||||
EnableSplitCore = _config.SplitCoreItem.EnableSplitCore;
|
||||
RouteSplitCoreType = _config.SplitCoreItem.RouteCoreType.ToString();
|
||||
|
||||
_config.SplitCoreItem.SplitCoreTypes.ForEach(it =>
|
||||
{
|
||||
var type = it.CoreType.ToString();
|
||||
switch ((int)it.ConfigType)
|
||||
{
|
||||
case 1:
|
||||
SplitCoreType1 = type;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
SplitCoreType3 = type;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
SplitCoreType4 = type;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
SplitCoreType5 = type;
|
||||
break;
|
||||
|
||||
case 6:
|
||||
SplitCoreType6 = type;
|
||||
break;
|
||||
|
||||
case 7:
|
||||
SplitCoreType7 = type;
|
||||
break;
|
||||
|
||||
case 8:
|
||||
SplitCoreType8 = type;
|
||||
break;
|
||||
|
||||
case 9:
|
||||
SplitCoreType9 = type;
|
||||
break;
|
||||
}
|
||||
});
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task SaveSettingAsync()
|
||||
{
|
||||
if (localPort.ToString().IsNullOrEmpty() || !Utils.IsNumeric(localPort.ToString())
|
||||
|
|
@ -362,6 +459,7 @@ public class OptionSettingViewModel : MyReactiveObject
|
|||
|
||||
//coreType
|
||||
await SaveCoreType();
|
||||
await SaveSplitCoreType();
|
||||
|
||||
if (await ConfigHandler.SaveConfig(_config) == 0)
|
||||
{
|
||||
|
|
@ -420,4 +518,46 @@ public class OptionSettingViewModel : MyReactiveObject
|
|||
}
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task SaveSplitCoreType()
|
||||
{
|
||||
for (int k = 1; k <= _config.SplitCoreItem.SplitCoreTypes.Count; k++)
|
||||
{
|
||||
var item = _config.SplitCoreItem.SplitCoreTypes[k - 1];
|
||||
var type = string.Empty;
|
||||
switch ((int)item.ConfigType)
|
||||
{
|
||||
case 1:
|
||||
type = SplitCoreType1;
|
||||
break;
|
||||
case 3:
|
||||
type = SplitCoreType3;
|
||||
break;
|
||||
case 4:
|
||||
type = SplitCoreType4;
|
||||
break;
|
||||
case 5:
|
||||
type = SplitCoreType5;
|
||||
break;
|
||||
case 6:
|
||||
type = SplitCoreType6;
|
||||
break;
|
||||
case 7:
|
||||
type = SplitCoreType7;
|
||||
break;
|
||||
case 8:
|
||||
type = SplitCoreType8;
|
||||
break;
|
||||
case 9:
|
||||
type = SplitCoreType9;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
item.CoreType = (ECoreType)Enum.Parse(typeof(ECoreType), type);
|
||||
}
|
||||
_config.SplitCoreItem.RouteCoreType = (ECoreType)Enum.Parse(typeof(ECoreType), RouteSplitCoreType);
|
||||
_config.SplitCoreItem.EnableSplitCore = EnableSplitCore;
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -770,7 +770,8 @@ public class ProfilesViewModel : MyReactiveObject
|
|||
}
|
||||
if (blClipboard)
|
||||
{
|
||||
var result = await CoreConfigHandler.GenerateClientConfig(item, null);
|
||||
var coreLaunchContext = new CoreLaunchContext(item, _config);
|
||||
var result = await CoreConfigHandler.GenerateClientConfig(coreLaunchContext, null);
|
||||
if (result.Success != true)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(result.Msg);
|
||||
|
|
@ -793,7 +794,8 @@ public class ProfilesViewModel : MyReactiveObject
|
|||
{
|
||||
return;
|
||||
}
|
||||
var result = await CoreConfigHandler.GenerateClientConfig(item, fileName);
|
||||
var coreLaunchContext = new CoreLaunchContext(item, _config);
|
||||
var result = await CoreConfigHandler.GenerateClientConfig(coreLaunchContext, fileName);
|
||||
if (result.Success != true)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(result.Msg);
|
||||
|
|
|
|||
|
|
@ -553,6 +553,151 @@
|
|||
Width="400"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
</Grid>
|
||||
<Grid
|
||||
x:Name="gridNaive"
|
||||
Grid.Row="2"
|
||||
ColumnDefinitions="180,Auto"
|
||||
IsVisible="False"
|
||||
RowDefinitions="Auto,Auto,Auto">
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbId3}" />
|
||||
<TextBox
|
||||
x:Name="txtId100"
|
||||
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.TbHeaderType100}" />
|
||||
<ComboBox
|
||||
x:Name="cmbHeaderType100"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
</Grid>
|
||||
<Grid
|
||||
x:Name="gridJuicity"
|
||||
Grid.Row="2"
|
||||
ColumnDefinitions="180,Auto"
|
||||
IsVisible="False"
|
||||
RowDefinitions="Auto,Auto,Auto,Auto">
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbId}" />
|
||||
<TextBox
|
||||
x:Name="txtId101"
|
||||
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.TbId3}" />
|
||||
<TextBox
|
||||
x:Name="txtSecurity101"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Width="400"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbHeaderType8}" />
|
||||
<ComboBox
|
||||
x:Name="cmbHeaderType101"
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
</Grid>
|
||||
<Grid
|
||||
x:Name="gridBrook"
|
||||
Grid.Row="2"
|
||||
ColumnDefinitions="180,Auto"
|
||||
IsVisible="False"
|
||||
RowDefinitions="Auto,Auto,Auto">
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbId3}" />
|
||||
<TextBox
|
||||
x:Name="txtId102"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Width="400"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
</Grid>
|
||||
<Grid
|
||||
x:Name="gridShadowquic"
|
||||
Grid.Row="2"
|
||||
ColumnDefinitions="180,Auto"
|
||||
IsVisible="False"
|
||||
RowDefinitions="Auto,Auto,Auto,Auto">
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbId}" />
|
||||
<TextBox
|
||||
x:Name="txtId103"
|
||||
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.TbId3}" />
|
||||
<TextBox
|
||||
x:Name="txtSecurity103"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Width="400"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbHeaderType8}" />
|
||||
<ComboBox
|
||||
x:Name="cmbHeaderType103"
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
</Grid>
|
||||
|
||||
<Separator
|
||||
x:Name="sepa2"
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
|||
gridHysteria2.IsVisible = true;
|
||||
sepa2.IsVisible = false;
|
||||
gridTransport.IsVisible = false;
|
||||
cmbCoreType.IsEnabled = false;
|
||||
cmbCoreType.ItemsSource = Global.Hysteria2CoreTypes.AppendEmpty();
|
||||
cmbFingerprint.IsEnabled = false;
|
||||
cmbFingerprint.SelectedValue = string.Empty;
|
||||
break;
|
||||
|
|
@ -87,11 +87,11 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
|||
gridTuic.IsVisible = true;
|
||||
sepa2.IsVisible = false;
|
||||
gridTransport.IsVisible = false;
|
||||
cmbCoreType.IsEnabled = false;
|
||||
cmbCoreType.ItemsSource = Global.TuicCoreTypes.AppendEmpty();
|
||||
cmbFingerprint.IsEnabled = false;
|
||||
cmbFingerprint.SelectedValue = string.Empty;
|
||||
|
||||
cmbHeaderType8.ItemsSource = Global.TuicCongestionControls;
|
||||
cmbHeaderType8.ItemsSource = Global.CongestionControls;
|
||||
break;
|
||||
|
||||
case EConfigType.WireGuard:
|
||||
|
|
@ -108,6 +108,49 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
|||
lstStreamSecurity.Add(Global.StreamSecurityReality);
|
||||
cmbCoreType.IsEnabled = false;
|
||||
break;
|
||||
|
||||
case EConfigType.NaiveProxy:
|
||||
gridNaive.IsVisible = true;
|
||||
sepa2.IsVisible = false;
|
||||
gridTransport.IsVisible = false;
|
||||
cmbAlpn.IsEnabled = false;
|
||||
cmbFingerprint.IsEnabled = false;
|
||||
cmbFingerprint.SelectedValue = string.Empty;
|
||||
cmbCoreType.IsEnabled = false;
|
||||
|
||||
cmbHeaderType100.ItemsSource = Global.NaiveProxyProtocols;
|
||||
break;
|
||||
|
||||
case EConfigType.Juicity:
|
||||
gridJuicity.IsVisible = true;
|
||||
sepa2.IsVisible = false;
|
||||
gridTransport.IsVisible = false;
|
||||
cmbAlpn.IsEnabled = false;
|
||||
cmbFingerprint.IsEnabled = false;
|
||||
cmbFingerprint.SelectedValue = string.Empty;
|
||||
cmbCoreType.IsEnabled = false;
|
||||
|
||||
cmbHeaderType101.ItemsSource = Global.CongestionControls;
|
||||
break;
|
||||
|
||||
case EConfigType.Brook:
|
||||
gridBrook.IsVisible = true;
|
||||
sepa2.IsVisible = false;
|
||||
gridTransport.IsVisible = false;
|
||||
gridTls.IsVisible = false;
|
||||
cmbCoreType.IsEnabled = false;
|
||||
break;
|
||||
|
||||
case EConfigType.Shadowquic:
|
||||
gridShadowquic.IsVisible = true;
|
||||
sepa2.IsVisible = false;
|
||||
gridTransport.IsVisible = false;
|
||||
cmbFingerprint.IsEnabled = false;
|
||||
cmbFingerprint.SelectedValue = string.Empty;
|
||||
cmbCoreType.IsEnabled = false;
|
||||
|
||||
cmbHeaderType103.ItemsSource = Global.CongestionControls;
|
||||
break;
|
||||
}
|
||||
cmbStreamSecurity.ItemsSource = lstStreamSecurity;
|
||||
|
||||
|
|
@ -177,6 +220,27 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
|||
case EConfigType.Anytls:
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId10.Text).DisposeWith(disposables);
|
||||
break;
|
||||
|
||||
case EConfigType.NaiveProxy:
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId100.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType100.SelectedValue).DisposeWith(disposables);
|
||||
break;
|
||||
|
||||
case EConfigType.Juicity:
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId101.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.txtSecurity101.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType101.SelectedValue).DisposeWith(disposables);
|
||||
break;
|
||||
|
||||
case EConfigType.Brook:
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId102.Text).DisposeWith(disposables);
|
||||
break;
|
||||
|
||||
case EConfigType.Shadowquic:
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId103.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.txtSecurity103.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType103.SelectedValue).DisposeWith(disposables);
|
||||
break;
|
||||
}
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType.SelectedValue).DisposeWith(disposables);
|
||||
|
|
|
|||
|
|
@ -47,6 +47,11 @@
|
|||
<MenuItem x:Name="menuAddHysteria2Server" Header="{x:Static resx:ResUI.menuAddHysteria2Server}" />
|
||||
<MenuItem x:Name="menuAddTuicServer" Header="{x:Static resx:ResUI.menuAddTuicServer}" />
|
||||
<MenuItem x:Name="menuAddAnytlsServer" Header="{x:Static resx:ResUI.menuAddAnytlsServer}" />
|
||||
<Separator />
|
||||
<MenuItem x:Name="menuAddBrookServer" Header="{x:Static resx:ResUI.menuAddBrookServer}" />
|
||||
<MenuItem x:Name="menuAddJuicityServer" Header="{x:Static resx:ResUI.menuAddJuicityServer}" />
|
||||
<MenuItem x:Name="menuAddNaiveServer" Header="{x:Static resx:ResUI.menuAddNaiveServer}" />
|
||||
<MenuItem x:Name="menuAddShadowquicServer" Header="{x:Static resx:ResUI.menuAddShadowquicServer}" />
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem Padding="8,0">
|
||||
|
|
|
|||
|
|
@ -84,6 +84,10 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
|
|||
this.BindCommand(ViewModel, vm => vm.AddTuicServerCmd, v => v.menuAddTuicServer).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.AddWireguardServerCmd, v => v.menuAddWireguardServer).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.AddAnytlsServerCmd, v => v.menuAddAnytlsServer).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.AddBrookServerCmd, v => v.menuAddBrookServer).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.AddJuicityServerCmd, v => v.menuAddJuicityServer).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.AddNaiveServerCmd, v => v.menuAddNaiveServer).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.AddShadowquicServerCmd, v => v.menuAddShadowquicServer).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.AddCustomServerCmd, v => v.menuAddCustomServer).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.AddServerViaClipboardCmd, v => v.menuAddServerViaClipboard).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.AddServerViaScanCmd, v => v.menuAddServerViaScan).DisposeWith(disposables);
|
||||
|
|
|
|||
|
|
@ -802,101 +802,298 @@
|
|||
</TabItem>
|
||||
|
||||
<TabItem Header="{x:Static resx:ResUI.TbSettingsCoreType}">
|
||||
<Grid
|
||||
Margin="{StaticResource Margin4}"
|
||||
ColumnDefinitions="Auto,Auto"
|
||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="VMess" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreType1"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
<ScrollViewer VerticalScrollBarVisibility="Visible">
|
||||
<Grid Margin="{StaticResource Margin4}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="Custom" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreType2"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
<Grid
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="Shadowsocks" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreType3"
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="VMess" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreType1"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="Socks" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreType4"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="Custom" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreType2"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="5"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="VLESS" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreType5"
|
||||
Grid.Row="5"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="Shadowsocks" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreType3"
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="6"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="Trojan" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreType6"
|
||||
Grid.Row="6"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="Socks" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreType4"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="7"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="Wireguard" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreType9"
|
||||
Grid.Row="7"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
</Grid>
|
||||
<TextBlock
|
||||
Grid.Row="5"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="VLESS" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreType5"
|
||||
Grid.Row="5"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="6"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="Trojan" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreType6"
|
||||
Grid.Row="6"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="7"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="Wireguard" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreType9"
|
||||
Grid.Row="7"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
</Grid>
|
||||
|
||||
<Grid
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin4}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="2"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsSplitCoreDoc1}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="2"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsSplitCoreDoc2}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsSplitCoreEnable}" />
|
||||
<ToggleSwitch
|
||||
x:Name="togCoreSplit"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="路由 Core" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreSplitRouteType"
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="VMess" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreSplitType1"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="5"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="Shadowsocks" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreSplitType3"
|
||||
Grid.Row="5"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="6"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="Socks" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreSplitType4"
|
||||
Grid.Row="6"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="7"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="VLESS" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreSplitType5"
|
||||
Grid.Row="7"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="8"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="Trojan" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreSplitType6"
|
||||
Grid.Row="8"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="9"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="Hysteria2" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreSplitType7"
|
||||
Grid.Row="9"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="10"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="TUIC" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreSplitType8"
|
||||
Grid.Row="10"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="11"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="Wireguard" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreSplitType9"
|
||||
Grid.Row="11"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
</DockPanel>
|
||||
|
|
|
|||
|
|
@ -41,6 +41,17 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
|
|||
cmbCoreType6.ItemsSource = Global.CoreTypes;
|
||||
cmbCoreType9.ItemsSource = Global.CoreTypes;
|
||||
|
||||
cmbCoreSplitRouteType.ItemsSource = Global.CoreTypes;
|
||||
|
||||
cmbCoreSplitType1.ItemsSource = Global.CoreTypes;
|
||||
cmbCoreSplitType3.ItemsSource = Global.CoreTypes;
|
||||
cmbCoreSplitType4.ItemsSource = Global.CoreTypes;
|
||||
cmbCoreSplitType5.ItemsSource = Global.CoreTypes;
|
||||
cmbCoreSplitType6.ItemsSource = Global.CoreTypes;
|
||||
cmbCoreSplitType7.ItemsSource = Global.Hysteria2CoreTypes;
|
||||
cmbCoreSplitType8.ItemsSource = Global.TuicCoreTypes;
|
||||
cmbCoreSplitType9.ItemsSource = Global.CoreTypes;
|
||||
|
||||
cmbMixedConcurrencyCount.ItemsSource = Enumerable.Range(2, 7).ToList();
|
||||
cmbSpeedTestTimeout.ItemsSource = Enumerable.Range(2, 5).Select(i => i * 5).ToList();
|
||||
cmbSpeedTestUrl.ItemsSource = Global.SpeedTestUrls;
|
||||
|
|
@ -119,6 +130,18 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
|
|||
this.Bind(ViewModel, vm => vm.CoreType6, v => v.cmbCoreType6.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.CoreType9, v => v.cmbCoreType9.SelectedValue).DisposeWith(disposables);
|
||||
|
||||
this.Bind(ViewModel, vm => vm.EnableSplitCore, v => v.togCoreSplit.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.RouteSplitCoreType, v => v.cmbCoreSplitRouteType.SelectedValue).DisposeWith(disposables);
|
||||
|
||||
this.Bind(ViewModel, vm => vm.SplitCoreType1, v => v.cmbCoreSplitType1.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SplitCoreType3, v => v.cmbCoreSplitType3.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SplitCoreType4, v => v.cmbCoreSplitType4.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SplitCoreType5, v => v.cmbCoreSplitType5.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SplitCoreType6, v => v.cmbCoreSplitType6.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SplitCoreType7, v => v.cmbCoreSplitType7.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SplitCoreType8, v => v.cmbCoreSplitType8.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SplitCoreType9, v => v.cmbCoreSplitType9.SelectedValue).DisposeWith(disposables);
|
||||
|
||||
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -736,6 +736,199 @@
|
|||
Margin="{StaticResource Margin4}"
|
||||
Style="{StaticResource DefTextBox}" />
|
||||
</Grid>
|
||||
<Grid
|
||||
x:Name="gridNaive"
|
||||
Grid.Row="2"
|
||||
Visibility="Hidden">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="180" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbId3}" />
|
||||
<TextBox
|
||||
x:Name="txtId100"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Width="400"
|
||||
Margin="{StaticResource Margin4}"
|
||||
Style="{StaticResource DefTextBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbHeaderType100}" />
|
||||
<ComboBox
|
||||
x:Name="cmbHeaderType100"
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
</Grid>
|
||||
<Grid
|
||||
x:Name="gridJuicity"
|
||||
Grid.Row="2"
|
||||
Visibility="Hidden">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="180" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbId}" />
|
||||
<TextBox
|
||||
x:Name="txtId101"
|
||||
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.TbId3}" />
|
||||
<TextBox
|
||||
x:Name="txtSecurity101"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Width="400"
|
||||
Margin="{StaticResource Margin4}"
|
||||
Style="{StaticResource DefTextBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbHeaderType8}" />
|
||||
<ComboBox
|
||||
x:Name="cmbHeaderType101"
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
</Grid>
|
||||
<Grid
|
||||
x:Name="gridBrook"
|
||||
Grid.Row="2"
|
||||
Visibility="Hidden">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="180" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbId3}" />
|
||||
<TextBox
|
||||
x:Name="txtId102"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Width="400"
|
||||
Margin="{StaticResource Margin4}"
|
||||
Style="{StaticResource DefTextBox}" />
|
||||
</Grid>
|
||||
<Grid
|
||||
x:Name="gridShadowquic"
|
||||
Grid.Row="2"
|
||||
Visibility="Hidden">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="180" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbId}" />
|
||||
<TextBox
|
||||
x:Name="txtId103"
|
||||
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.TbId3}" />
|
||||
<TextBox
|
||||
x:Name="txtSecurity103"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Width="400"
|
||||
Margin="{StaticResource Margin4}"
|
||||
Style="{StaticResource DefTextBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbHeaderType8}" />
|
||||
<ComboBox
|
||||
x:Name="cmbHeaderType103"
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
</Grid>
|
||||
|
||||
<Separator
|
||||
x:Name="sepa2"
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ public partial class AddServerWindow
|
|||
gridHysteria2.Visibility = Visibility.Visible;
|
||||
sepa2.Visibility = Visibility.Collapsed;
|
||||
gridTransport.Visibility = Visibility.Collapsed;
|
||||
cmbCoreType.IsEnabled = false;
|
||||
cmbCoreType.ItemsSource = Global.Hysteria2CoreTypes.AppendEmpty();
|
||||
cmbFingerprint.IsEnabled = false;
|
||||
cmbFingerprint.Text = string.Empty;
|
||||
break;
|
||||
|
|
@ -81,11 +81,11 @@ public partial class AddServerWindow
|
|||
gridTuic.Visibility = Visibility.Visible;
|
||||
sepa2.Visibility = Visibility.Collapsed;
|
||||
gridTransport.Visibility = Visibility.Collapsed;
|
||||
cmbCoreType.IsEnabled = false;
|
||||
cmbCoreType.ItemsSource = Global.TuicCoreTypes.AppendEmpty();
|
||||
cmbFingerprint.IsEnabled = false;
|
||||
cmbFingerprint.Text = string.Empty;
|
||||
|
||||
cmbHeaderType8.ItemsSource = Global.TuicCongestionControls;
|
||||
cmbHeaderType8.ItemsSource = Global.CongestionControls;
|
||||
break;
|
||||
|
||||
case EConfigType.WireGuard:
|
||||
|
|
@ -102,6 +102,49 @@ public partial class AddServerWindow
|
|||
cmbCoreType.IsEnabled = false;
|
||||
lstStreamSecurity.Add(Global.StreamSecurityReality);
|
||||
break;
|
||||
|
||||
case EConfigType.NaiveProxy:
|
||||
gridNaive.Visibility = Visibility.Visible;
|
||||
sepa2.Visibility = Visibility.Collapsed;
|
||||
gridTransport.Visibility = Visibility.Collapsed;
|
||||
cmbAlpn.IsEnabled = false;
|
||||
cmbFingerprint.IsEnabled = false;
|
||||
cmbFingerprint.Text = string.Empty;
|
||||
cmbCoreType.IsEnabled = false;
|
||||
|
||||
cmbHeaderType100.ItemsSource = Global.NaiveProxyProtocols;
|
||||
break;
|
||||
|
||||
case EConfigType.Juicity:
|
||||
gridJuicity.Visibility = Visibility.Visible;
|
||||
sepa2.Visibility = Visibility.Collapsed;
|
||||
gridTransport.Visibility = Visibility.Collapsed;
|
||||
cmbAlpn.IsEnabled = false;
|
||||
cmbFingerprint.IsEnabled = false;
|
||||
cmbFingerprint.Text = string.Empty;
|
||||
cmbCoreType.IsEnabled = false;
|
||||
|
||||
cmbHeaderType101.ItemsSource = Global.CongestionControls;
|
||||
break;
|
||||
|
||||
case EConfigType.Brook:
|
||||
gridBrook.Visibility = Visibility.Visible;
|
||||
sepa2.Visibility = Visibility.Collapsed;
|
||||
gridTransport.Visibility = Visibility.Collapsed;
|
||||
gridTls.Visibility = Visibility.Collapsed;
|
||||
cmbCoreType.IsEnabled = false;
|
||||
break;
|
||||
|
||||
case EConfigType.Shadowquic:
|
||||
gridShadowquic.Visibility = Visibility.Visible;
|
||||
sepa2.Visibility = Visibility.Collapsed;
|
||||
gridTransport.Visibility = Visibility.Collapsed;
|
||||
cmbFingerprint.IsEnabled = false;
|
||||
cmbFingerprint.Text = string.Empty;
|
||||
cmbCoreType.IsEnabled = false;
|
||||
|
||||
cmbHeaderType103.ItemsSource = Global.CongestionControls;
|
||||
break;
|
||||
}
|
||||
cmbStreamSecurity.ItemsSource = lstStreamSecurity;
|
||||
|
||||
|
|
@ -171,6 +214,27 @@ public partial class AddServerWindow
|
|||
case EConfigType.Anytls:
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId10.Text).DisposeWith(disposables);
|
||||
break;
|
||||
|
||||
case EConfigType.NaiveProxy:
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId100.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType100.Text).DisposeWith(disposables);
|
||||
break;
|
||||
|
||||
case EConfigType.Juicity:
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId101.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.txtSecurity101.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType101.Text).DisposeWith(disposables);
|
||||
break;
|
||||
|
||||
case EConfigType.Brook:
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId102.Text).DisposeWith(disposables);
|
||||
break;
|
||||
|
||||
case EConfigType.Shadowquic:
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId103.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.txtSecurity103.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType103.Text).DisposeWith(disposables);
|
||||
break;
|
||||
}
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType.Text).DisposeWith(disposables);
|
||||
|
|
|
|||
|
|
@ -112,6 +112,23 @@
|
|||
x:Name="menuAddAnytlsServer"
|
||||
Height="{StaticResource MenuItemHeight}"
|
||||
Header="{x:Static resx:ResUI.menuAddAnytlsServer}" />
|
||||
<Separator Margin="-40,5" />
|
||||
<MenuItem
|
||||
x:Name="menuAddBrookServer"
|
||||
Height="{StaticResource MenuItemHeight}"
|
||||
Header="{x:Static resx:ResUI.menuAddBrookServer}" />
|
||||
<MenuItem
|
||||
x:Name="menuAddJuicityServer"
|
||||
Height="{StaticResource MenuItemHeight}"
|
||||
Header="{x:Static resx:ResUI.menuAddJuicityServer}" />
|
||||
<MenuItem
|
||||
x:Name="menuAddNaiveServer"
|
||||
Height="{StaticResource MenuItemHeight}"
|
||||
Header="{x:Static resx:ResUI.menuAddNaiveServer}" />
|
||||
<MenuItem
|
||||
x:Name="menuAddShadowquicServer"
|
||||
Height="{StaticResource MenuItemHeight}"
|
||||
Header="{x:Static resx:ResUI.menuAddShadowquicServer}" />
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
<Separator />
|
||||
|
|
|
|||
|
|
@ -81,6 +81,10 @@ public partial class MainWindow
|
|||
this.BindCommand(ViewModel, vm => vm.AddTuicServerCmd, v => v.menuAddTuicServer).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.AddWireguardServerCmd, v => v.menuAddWireguardServer).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.AddAnytlsServerCmd, v => v.menuAddAnytlsServer).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.AddBrookServerCmd, v => v.menuAddBrookServer).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.AddJuicityServerCmd, v => v.menuAddJuicityServer).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.AddNaiveServerCmd, v => v.menuAddNaiveServer).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.AddShadowquicServerCmd, v => v.menuAddShadowquicServer).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.AddCustomServerCmd, v => v.menuAddCustomServer).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.AddServerViaClipboardCmd, v => v.menuAddServerViaClipboard).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.AddServerViaScanCmd, v => v.menuAddServerViaScan).DisposeWith(disposables);
|
||||
|
|
|
|||
|
|
@ -1107,126 +1107,329 @@
|
|||
</TabItem>
|
||||
|
||||
<TabItem Header="{x:Static resx:ResUI.TbSettingsCoreType}">
|
||||
<Grid Margin="{StaticResource Margin8}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="VMess" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreType1"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
<ScrollViewer VerticalScrollBarVisibility="Visible">
|
||||
<Grid Margin="{StaticResource Margin8}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="VMess" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreType1"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="Custom" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreType2"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="Custom" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreType2"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="Shadowsocks" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreType3"
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="Shadowsocks" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreType3"
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="Socks" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreType4"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="Socks" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreType4"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="5"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="VLESS" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreType5"
|
||||
Grid.Row="5"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
<TextBlock
|
||||
Grid.Row="5"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="VLESS" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreType5"
|
||||
Grid.Row="5"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="6"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="Trojan" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreType6"
|
||||
Grid.Row="6"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
<TextBlock
|
||||
Grid.Row="6"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="Trojan" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreType6"
|
||||
Grid.Row="6"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="7"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="Wireguard" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreType9"
|
||||
Grid.Row="7"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
</Grid>
|
||||
<TextBlock
|
||||
Grid.Row="7"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="Wireguard" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreType9"
|
||||
Grid.Row="7"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
</Grid>
|
||||
<Grid
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin8}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="2"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSettingsSplitCoreDoc1}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="2"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSettingsSplitCoreDoc2}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSettingsSplitCoreEnable}" />
|
||||
<ToggleButton
|
||||
x:Name="togCoreSplit"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin8}"
|
||||
HorizontalAlignment="Left" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="路由 Core" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreSplitRouteType"
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="VMess" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreSplitType1"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="5"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="Shadowsocks" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreSplitType3"
|
||||
Grid.Row="5"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="6"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="Socks" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreSplitType4"
|
||||
Grid.Row="6"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="7"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="VLESS" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreSplitType5"
|
||||
Grid.Row="7"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="8"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="Trojan" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreSplitType6"
|
||||
Grid.Row="8"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="9"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="Hysteria2" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreSplitType7"
|
||||
Grid.Row="9"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="10"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="TUIC" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreSplitType8"
|
||||
Grid.Row="10"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="11"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="Wireguard" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreSplitType9"
|
||||
Grid.Row="11"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
</DockPanel>
|
||||
|
|
|
|||
|
|
@ -43,6 +43,17 @@ public partial class OptionSettingWindow
|
|||
cmbCoreType6.ItemsSource = Global.CoreTypes;
|
||||
cmbCoreType9.ItemsSource = Global.CoreTypes;
|
||||
|
||||
cmbCoreSplitRouteType.ItemsSource = Global.CoreTypes;
|
||||
|
||||
cmbCoreSplitType1.ItemsSource = Global.CoreTypes;
|
||||
cmbCoreSplitType3.ItemsSource = Global.CoreTypes;
|
||||
cmbCoreSplitType4.ItemsSource = Global.CoreTypes;
|
||||
cmbCoreSplitType5.ItemsSource = Global.CoreTypes;
|
||||
cmbCoreSplitType6.ItemsSource = Global.CoreTypes;
|
||||
cmbCoreSplitType7.ItemsSource = Global.Hysteria2CoreTypes;
|
||||
cmbCoreSplitType8.ItemsSource = Global.TuicCoreTypes;
|
||||
cmbCoreSplitType9.ItemsSource = Global.CoreTypes;
|
||||
|
||||
cmbMixedConcurrencyCount.ItemsSource = Enumerable.Range(2, 7).ToList();
|
||||
cmbSpeedTestTimeout.ItemsSource = Enumerable.Range(2, 5).Select(i => i * 5).ToList();
|
||||
cmbSpeedTestUrl.ItemsSource = Global.SpeedTestUrls;
|
||||
|
|
@ -131,6 +142,18 @@ public partial class OptionSettingWindow
|
|||
this.Bind(ViewModel, vm => vm.CoreType6, v => v.cmbCoreType6.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.CoreType9, v => v.cmbCoreType9.Text).DisposeWith(disposables);
|
||||
|
||||
this.Bind(ViewModel, vm => vm.EnableSplitCore, v => v.togCoreSplit.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.RouteSplitCoreType, v => v.cmbCoreSplitRouteType.Text).DisposeWith(disposables);
|
||||
|
||||
this.Bind(ViewModel, vm => vm.SplitCoreType1, v => v.cmbCoreSplitType1.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SplitCoreType3, v => v.cmbCoreSplitType3.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SplitCoreType4, v => v.cmbCoreSplitType4.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SplitCoreType5, v => v.cmbCoreSplitType5.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SplitCoreType6, v => v.cmbCoreSplitType6.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SplitCoreType7, v => v.cmbCoreSplitType7.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SplitCoreType8, v => v.cmbCoreSplitType8.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SplitCoreType9, v => v.cmbCoreSplitType9.Text).DisposeWith(disposables);
|
||||
|
||||
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
||||
});
|
||||
WindowsUtils.SetDarkBorder(this, AppHandler.Instance.Config.UiItem.CurrentTheme);
|
||||
|
|
|
|||
Loading…
Reference in a new issue