mirror of
https://github.com/2dust/v2rayN.git
synced 2026-04-18 05:25:46 +00:00
Compare commits
3 commits
0d06f1d63b
...
04928c7244
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
04928c7244 | ||
|
|
79241530af | ||
|
|
6eedb7b37f |
40 changed files with 589 additions and 548 deletions
|
|
@ -497,13 +497,6 @@ public class Utils
|
|||
return false;
|
||||
}
|
||||
|
||||
var ext = Path.GetExtension(domain);
|
||||
if (ext.IsNotEmpty()
|
||||
&& ext[1..].ToLowerInvariant() is "json" or "txt" or "xml" or "cfg" or "ini" or "log" or "yaml" or "yml" or "toml")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Uri.CheckHostName(domain) == UriHostNameType.Dns;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,7 +49,6 @@ public class Global
|
|||
public const string DirectTag = "direct";
|
||||
public const string BlockTag = "block";
|
||||
public const string DnsTag = "dns-module";
|
||||
public const string DirectDnsTag = "direct-dns";
|
||||
public const string BalancerTagSuffix = "-round";
|
||||
public const string StreamSecurity = "tls";
|
||||
public const string StreamSecurityReality = "reality";
|
||||
|
|
@ -91,22 +90,6 @@ public class Global
|
|||
|
||||
public const int Hysteria2DefaultHopInt = 10;
|
||||
|
||||
public const string PolicyGroupExcludeKeywords = @"剩余|过期|到期|重置|[Rr]emaining|[Ee]xpir|[Rr]eset";
|
||||
|
||||
public const string PolicyGroupDefaultAllFilter = $"^(?!.*(?:{PolicyGroupExcludeKeywords})).*$";
|
||||
|
||||
public static readonly List<string> PolicyGroupDefaultFilterList =
|
||||
[
|
||||
// All nodes (exclude traffic/expiry info)
|
||||
PolicyGroupDefaultAllFilter,
|
||||
// Low multiplier nodes, e.g. ×0.1, 0.5x, 0.1倍
|
||||
@"^.*(?:[×xX✕*]\s*0\.[0-9]+|0\.[0-9]+\s*[×xX✕*倍]).*$",
|
||||
// Dedicated line nodes, e.g. IPLC, IEPL
|
||||
$@"^(?!.*(?:{PolicyGroupExcludeKeywords})).*(?:专线|IPLC|IEPL|中转).*$",
|
||||
// Japan nodes
|
||||
$@"^(?!.*(?:{PolicyGroupExcludeKeywords})).*(?:日本|\\b[Jj][Pp]\\b|🇯🇵|[Jj]apan).*$",
|
||||
];
|
||||
|
||||
public static readonly List<string> IEProxyProtocols =
|
||||
[
|
||||
"{ip}:{http_port}",
|
||||
|
|
|
|||
|
|
@ -5,39 +5,6 @@ public record CoreConfigContextBuilderResult(CoreConfigContext Context, NodeVali
|
|||
public bool Success => ValidatorResult.Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Holds the results of a full context build, including the main context and an optional
|
||||
/// pre-socks context (e.g. for TUN protection or pre-socks chaining).
|
||||
/// </summary>
|
||||
public record CoreConfigContextBuilderAllResult(
|
||||
CoreConfigContextBuilderResult MainResult,
|
||||
CoreConfigContextBuilderResult? PreSocksResult)
|
||||
{
|
||||
/// <summary>True only when both the main result and (if present) the pre-socks result succeeded.</summary>
|
||||
public bool Success => MainResult.Success && (PreSocksResult?.Success ?? true);
|
||||
|
||||
/// <summary>
|
||||
/// Merges all errors and warnings from the main result and the optional pre-socks result
|
||||
/// into a single <see cref="NodeValidatorResult"/> for unified notification.
|
||||
/// </summary>
|
||||
public NodeValidatorResult CombinedValidatorResult => new(
|
||||
[.. MainResult.ValidatorResult.Errors, .. PreSocksResult?.ValidatorResult.Errors ?? []],
|
||||
[.. MainResult.ValidatorResult.Warnings, .. PreSocksResult?.ValidatorResult.Warnings ?? []]);
|
||||
|
||||
/// <summary>
|
||||
/// The main context with TunProtectSsPort/ProxyRelaySsPort and ProtectDomainList merged in
|
||||
/// from the pre-socks result (if any). Pass this to the core runner.
|
||||
/// </summary>
|
||||
public CoreConfigContext ResolvedMainContext => PreSocksResult is not null
|
||||
? MainResult.Context with
|
||||
{
|
||||
TunProtectSsPort = PreSocksResult.Context.TunProtectSsPort,
|
||||
ProxyRelaySsPort = PreSocksResult.Context.ProxyRelaySsPort,
|
||||
ProtectDomainList = [.. MainResult.Context.ProtectDomainList ?? [], .. PreSocksResult.Context.ProtectDomainList ?? []],
|
||||
}
|
||||
: MainResult.Context;
|
||||
}
|
||||
|
||||
public class CoreConfigContextBuilder
|
||||
{
|
||||
/// <summary>
|
||||
|
|
@ -108,79 +75,6 @@ public class CoreConfigContextBuilder
|
|||
return new CoreConfigContextBuilderResult(context, validatorResult);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the main <see cref="CoreConfigContext"/> for <paramref name="node"/> and, when
|
||||
/// the main build succeeds, also builds the optional pre-socks context required for TUN
|
||||
/// protection or pre-socks proxy chaining.
|
||||
/// </summary>
|
||||
public static async Task<CoreConfigContextBuilderAllResult> BuildAll(Config config, ProfileItem node)
|
||||
{
|
||||
var mainResult = await Build(config, node);
|
||||
if (!mainResult.Success)
|
||||
{
|
||||
return new CoreConfigContextBuilderAllResult(mainResult, null);
|
||||
}
|
||||
|
||||
var preResult = await BuildPreSocksIfNeeded(mainResult.Context);
|
||||
return new CoreConfigContextBuilderAllResult(mainResult, preResult);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a pre-socks context is required for <paramref name="nodeContext"/>
|
||||
/// and, if so, builds and returns it. Returns <c>null</c> when no pre-socks core is needed.
|
||||
/// </summary>
|
||||
private static async Task<CoreConfigContextBuilderResult?> BuildPreSocksIfNeeded(CoreConfigContext nodeContext)
|
||||
{
|
||||
var config = nodeContext.AppConfig;
|
||||
var node = nodeContext.Node;
|
||||
var coreType = AppManager.Instance.GetCoreType(node, node.ConfigType);
|
||||
|
||||
var preSocksItem = ConfigHandler.GetPreSocksItem(config, node, coreType);
|
||||
if (preSocksItem != null)
|
||||
{
|
||||
var preSocksResult = await Build(nodeContext.AppConfig, preSocksItem);
|
||||
return preSocksResult with
|
||||
{
|
||||
Context = preSocksResult.Context with
|
||||
{
|
||||
ProtectDomainList = [.. nodeContext.ProtectDomainList ?? [], .. preSocksResult.Context.ProtectDomainList ?? []],
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (!nodeContext.IsTunEnabled
|
||||
|| coreType != ECoreType.Xray
|
||||
|| node.ConfigType == EConfigType.Custom)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var tunProtectSsPort = Utils.GetFreePort();
|
||||
var proxyRelaySsPort = Utils.GetFreePort();
|
||||
var preItem = new ProfileItem()
|
||||
{
|
||||
CoreType = ECoreType.sing_box,
|
||||
ConfigType = EConfigType.Shadowsocks,
|
||||
Address = Global.Loopback,
|
||||
Port = proxyRelaySsPort,
|
||||
Password = Global.None,
|
||||
};
|
||||
preItem.SetProtocolExtra(preItem.GetProtocolExtra() with
|
||||
{
|
||||
SsMethod = Global.None,
|
||||
});
|
||||
var preResult2 = await Build(nodeContext.AppConfig, preItem);
|
||||
return preResult2 with
|
||||
{
|
||||
Context = preResult2.Context with
|
||||
{
|
||||
ProtectDomainList = [.. nodeContext.ProtectDomainList ?? [], .. preResult2.Context.ProtectDomainList ?? []],
|
||||
TunProtectSsPort = tunProtectSsPort,
|
||||
ProxyRelaySsPort = proxyRelaySsPort,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves a node into the context, optionally wrapping it in a subscription-level proxy chain.
|
||||
/// Returns the effective (possibly replaced) node and the validation result.
|
||||
|
|
|
|||
|
|
@ -1198,28 +1198,46 @@ public static class ConfigHandler
|
|||
|
||||
/// <summary>
|
||||
/// Create a group server that combines multiple servers for load balancing
|
||||
/// Generates a PolicyGroup profile with references to the sub-items
|
||||
/// Generates a configuration file that references multiple servers
|
||||
/// </summary>
|
||||
/// <param name="config">Current configuration</param>
|
||||
/// <param name="subItem">Sub-item for grouping</param>
|
||||
/// <param name="selecteds">Selected servers to combine</param>
|
||||
/// <param name="coreType">Core type to use (Xray or sing_box)</param>
|
||||
/// <param name="multipleLoad">Load balancing algorithm</param>
|
||||
/// <returns>Result object with success state and data</returns>
|
||||
public static async Task<RetResult> AddGroupAllServer(Config config, SubItem? subItem)
|
||||
public static async Task<RetResult> AddGroupServer4Multiple(Config config, List<ProfileItem> selecteds, ECoreType coreType, EMultipleLoad multipleLoad, string? subId)
|
||||
{
|
||||
var result = new RetResult();
|
||||
|
||||
var subId = subItem?.Id;
|
||||
if (subId.IsNullOrEmpty())
|
||||
{
|
||||
result.Success = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
var indexId = Utils.GetGuid(false);
|
||||
var remark = $"{subItem.Remarks} - {ResUI.TbConfigTypePolicyGroup}";
|
||||
var childProfileIndexId = Utils.List2String(selecteds.Select(p => p.IndexId).ToList());
|
||||
|
||||
var remark = subId.IsNullOrEmpty() ? string.Empty : $"{(await AppManager.Instance.GetSubItem(subId))?.Remarks} ";
|
||||
if (coreType == ECoreType.Xray)
|
||||
{
|
||||
remark += multipleLoad switch
|
||||
{
|
||||
EMultipleLoad.LeastPing => ResUI.menuGenGroupMultipleServerXrayLeastPing,
|
||||
EMultipleLoad.Fallback => ResUI.menuGenGroupMultipleServerXrayFallback,
|
||||
EMultipleLoad.Random => ResUI.menuGenGroupMultipleServerXrayRandom,
|
||||
EMultipleLoad.RoundRobin => ResUI.menuGenGroupMultipleServerXrayRoundRobin,
|
||||
EMultipleLoad.LeastLoad => ResUI.menuGenGroupMultipleServerXrayLeastLoad,
|
||||
_ => ResUI.menuGenGroupMultipleServerXrayRoundRobin,
|
||||
};
|
||||
}
|
||||
else if (coreType == ECoreType.sing_box)
|
||||
{
|
||||
remark += multipleLoad switch
|
||||
{
|
||||
EMultipleLoad.LeastPing => ResUI.menuGenGroupMultipleServerSingBoxLeastPing,
|
||||
EMultipleLoad.Fallback => ResUI.menuGenGroupMultipleServerSingBoxFallback,
|
||||
_ => ResUI.menuGenGroupMultipleServerSingBoxLeastPing,
|
||||
};
|
||||
}
|
||||
var profile = new ProfileItem
|
||||
{
|
||||
IndexId = indexId,
|
||||
CoreType = ECoreType.Xray,
|
||||
CoreType = coreType,
|
||||
ConfigType = EConfigType.PolicyGroup,
|
||||
Remarks = remark,
|
||||
IsSub = false
|
||||
|
|
@ -1230,10 +1248,8 @@ public static class ConfigHandler
|
|||
}
|
||||
var extraItem = new ProtocolExtraItem
|
||||
{
|
||||
MultipleLoad = EMultipleLoad.LeastPing,
|
||||
GroupType = profile.ConfigType.ToString(),
|
||||
SubChildItems = subId,
|
||||
Filter = Global.PolicyGroupDefaultAllFilter,
|
||||
ChildItems = childProfileIndexId,
|
||||
MultipleLoad = multipleLoad,
|
||||
};
|
||||
profile.SetProtocolExtra(extraItem);
|
||||
var ret = await AddServerCommon(config, profile, true);
|
||||
|
|
@ -1242,92 +1258,6 @@ public static class ConfigHandler
|
|||
return result;
|
||||
}
|
||||
|
||||
private static string CombineWithDefaultAllFilter(string regionPattern)
|
||||
{
|
||||
return $"^(?!.*(?:{Global.PolicyGroupExcludeKeywords})).*(?:{regionPattern}).*$";
|
||||
}
|
||||
|
||||
private static readonly Dictionary<string, string> PolicyGroupRegionFilters = new()
|
||||
{
|
||||
{ "JP", "日本|\\b[Jj][Pp]\\b|🇯🇵|[Jj]apan" },
|
||||
{ "US", "美国|\\b[Uu][Ss]\\b|🇺🇸|[Uu]nited [Ss]tates|\\b[Uu][Ss][Aa]\\b" },
|
||||
{ "HK", "香港|\\b[Hh][Kk]\\b|🇭🇰|[Hh]ong ?[Kk]ong" },
|
||||
{ "TW", "台湾|台灣|\\b[Tt][Ww]\\b|🇹🇼|[Tt]aiwan" },
|
||||
{ "KR", "韩国|\\b[Kk][Rr]\\b|🇰🇷|[Kk]orea" },
|
||||
{ "SG", "新加坡|\\b[Ss][Gg]\\b|🇸🇬|[Ss]ingapore" },
|
||||
{ "DE", "德国|\\b[Dd][Ee]\\b|🇩🇪|[Gg]ermany" },
|
||||
{ "FR", "法国|\\b[Ff][Rr]\\b|🇫🇷|[Ff]rance" },
|
||||
{ "GB", "英国|\\b[Gg][Bb]\\b|🇬🇧|[Uu]nited [Kk]ingdom|[Bb]ritain" },
|
||||
{ "CA", "加拿大|🇨🇦|[Cc]anada" },
|
||||
{ "AU", "澳大利亚|\\b[Aa][Uu]\\b|🇦🇺|[Aa]ustralia" },
|
||||
{ "RU", "俄罗斯|\\b[Rr][Uu]\\b|🇷🇺|[Rr]ussia" },
|
||||
{ "BR", "巴西|\\b[Bb][Rr]\\b|🇧🇷|[Bb]razil" },
|
||||
{ "IN", "印度|🇮🇳|[Ii]ndia" },
|
||||
{ "VN", "越南|\\b[Vv][Nn]\\b|🇻🇳|[Vv]ietnam" },
|
||||
{ "ID", "印度尼西亚|\\b[Ii][Dd]\\b|🇮🇩|[Ii]ndonesia" },
|
||||
{ "MX", "墨西哥|\\b[Mm][Xx]\\b|🇲🇽|[Mm]exico" }
|
||||
};
|
||||
|
||||
public static async Task<RetResult> AddGroupRegionServer(Config config, SubItem? subItem)
|
||||
{
|
||||
var result = new RetResult();
|
||||
var subId = subItem?.Id;
|
||||
if (subId.IsNullOrEmpty())
|
||||
{
|
||||
result.Success = false;
|
||||
return result;
|
||||
}
|
||||
var childProfiles = await AppManager.Instance.ProfileItems(subId);
|
||||
List<string> indexIdList = [];
|
||||
|
||||
foreach (var regionFilter in PolicyGroupRegionFilters)
|
||||
{
|
||||
var indexId = Utils.GetGuid(false);
|
||||
var remark = $"{subItem.Remarks} - {ResUI.TbConfigTypePolicyGroup} - {regionFilter.Key}";
|
||||
var profile = new ProfileItem
|
||||
{
|
||||
IndexId = indexId,
|
||||
CoreType = ECoreType.Xray,
|
||||
ConfigType = EConfigType.PolicyGroup,
|
||||
Remarks = remark,
|
||||
IsSub = false
|
||||
};
|
||||
if (!subId.IsNullOrEmpty())
|
||||
{
|
||||
profile.Subid = subId;
|
||||
}
|
||||
var extraItem = new ProtocolExtraItem
|
||||
{
|
||||
MultipleLoad = EMultipleLoad.LeastPing,
|
||||
GroupType = profile.ConfigType.ToString(),
|
||||
SubChildItems = subId,
|
||||
Filter = CombineWithDefaultAllFilter(regionFilter.Value),
|
||||
};
|
||||
profile.SetProtocolExtra(extraItem);
|
||||
|
||||
var matchedChildProfiles = childProfiles?.Where(p =>
|
||||
p != null &&
|
||||
p.IsValid() &&
|
||||
!p.ConfigType.IsComplexType() &&
|
||||
(extraItem.Filter.IsNullOrEmpty() || Regex.IsMatch(p.Remarks, extraItem.Filter))
|
||||
)
|
||||
.ToList() ?? [];
|
||||
if (matchedChildProfiles.Count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var ret = await AddServerCommon(config, profile, true);
|
||||
if (ret == 0)
|
||||
{
|
||||
indexIdList.Add(indexId);
|
||||
}
|
||||
}
|
||||
result.Success = indexIdList.Count > 0;
|
||||
result.Data = indexIdList;
|
||||
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
|
||||
|
|
@ -1354,6 +1284,47 @@ public static class ConfigHandler
|
|||
return itemSocks;
|
||||
}
|
||||
|
||||
public static CoreConfigContext? GetPreSocksCoreConfigContext(CoreConfigContext nodeContext)
|
||||
{
|
||||
var config = nodeContext.AppConfig;
|
||||
var node = nodeContext.Node;
|
||||
var coreType = AppManager.Instance.GetCoreType(node, node.ConfigType);
|
||||
|
||||
var preSocksItem = GetPreSocksItem(config, node, coreType);
|
||||
if (preSocksItem != null)
|
||||
{
|
||||
return nodeContext with { Node = preSocksItem, };
|
||||
}
|
||||
|
||||
if ((!nodeContext.IsTunEnabled)
|
||||
|| coreType != ECoreType.Xray
|
||||
|| node.ConfigType == EConfigType.Custom)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var tunProtectSsPort = Utils.GetFreePort();
|
||||
var proxyRelaySsPort = Utils.GetFreePort();
|
||||
var preItem = new ProfileItem()
|
||||
{
|
||||
CoreType = ECoreType.sing_box,
|
||||
ConfigType = EConfigType.Shadowsocks,
|
||||
Address = Global.Loopback,
|
||||
Port = proxyRelaySsPort,
|
||||
Password = Global.None,
|
||||
};
|
||||
preItem.SetProtocolExtra(preItem.GetProtocolExtra() with
|
||||
{
|
||||
SsMethod = Global.None,
|
||||
});
|
||||
var preContext = nodeContext with
|
||||
{
|
||||
Node = preItem,
|
||||
TunProtectSsPort = tunProtectSsPort,
|
||||
ProxyRelaySsPort = proxyRelaySsPort,
|
||||
};
|
||||
return preContext;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove servers with invalid test results (timeout)
|
||||
/// Useful for cleaning up subscription lists
|
||||
|
|
|
|||
|
|
@ -99,9 +99,11 @@ public static class CoreConfigHandler
|
|||
};
|
||||
var builderResult = await CoreConfigContextBuilder.Build(config, dummyNode);
|
||||
var context = builderResult.Context;
|
||||
foreach (var testItem in selecteds)
|
||||
var ids = selecteds.Where(serverTestItem => !serverTestItem.IndexId.IsNullOrEmpty())
|
||||
.Select(serverTestItem => serverTestItem.IndexId);
|
||||
var nodes = await AppManager.Instance.GetProfileItemsByIndexIds(ids);
|
||||
foreach (var node in nodes)
|
||||
{
|
||||
var node = testItem.Profile;
|
||||
var (actNode, _) = await CoreConfigContextBuilder.ResolveNodeAsync(context, node, true);
|
||||
if (node.IndexId == actNode.IndexId)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -242,30 +242,6 @@ public sealed class AppManager
|
|||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<Dictionary<string, ProfileItem>> GetProfileItemsByIndexIdsAsMap(IEnumerable<string> indexIds)
|
||||
{
|
||||
var items = await GetProfileItemsByIndexIds(indexIds);
|
||||
return items.ToDictionary(it => it.IndexId);
|
||||
}
|
||||
|
||||
public async Task<List<ProfileItem>> GetProfileItemsOrderedByIndexIds(IEnumerable<string> indexIds)
|
||||
{
|
||||
var idList = indexIds.Where(id => !id.IsNullOrEmpty()).Distinct().ToList();
|
||||
if (idList.Count == 0)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
var items = await SQLiteHelper.Instance.TableAsync<ProfileItem>()
|
||||
.Where(it => idList.Contains(it.IndexId))
|
||||
.ToListAsync();
|
||||
var itemMap = items.ToDictionary(it => it.IndexId);
|
||||
|
||||
return idList.Select(id => itemMap.GetValueOrDefault(id))
|
||||
.Where(item => item != null)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public async Task<ProfileItem?> GetProfileItemViaRemarks(string? remarks)
|
||||
{
|
||||
if (remarks.IsNullOrEmpty())
|
||||
|
|
|
|||
|
|
@ -57,19 +57,27 @@ public class CoreManager
|
|||
}
|
||||
}
|
||||
|
||||
/// <param name="mainContext">Resolved main context (with pre-socks ports already merged if applicable).</param>
|
||||
/// <param name="preContext">Optional pre-socks context passed to <see cref="CoreStartPreService"/>.</param>
|
||||
public async Task LoadCore(CoreConfigContext? mainContext, CoreConfigContext? preContext)
|
||||
public async Task LoadCore(CoreConfigContext? context)
|
||||
{
|
||||
if (mainContext == null)
|
||||
if (context == null)
|
||||
{
|
||||
await UpdateFunc(false, ResUI.CheckServerSettings);
|
||||
return;
|
||||
}
|
||||
|
||||
var node = mainContext.Node;
|
||||
var contextMod = context;
|
||||
var node = contextMod.Node;
|
||||
var fileName = Utils.GetBinConfigPath(Global.CoreConfigFileName);
|
||||
var result = await CoreConfigHandler.GenerateClientConfig(mainContext, fileName);
|
||||
var preContext = ConfigHandler.GetPreSocksCoreConfigContext(contextMod);
|
||||
if (preContext is not null)
|
||||
{
|
||||
contextMod = contextMod with
|
||||
{
|
||||
TunProtectSsPort = preContext.TunProtectSsPort,
|
||||
ProxyRelaySsPort = preContext.ProxyRelaySsPort,
|
||||
};
|
||||
}
|
||||
var result = await CoreConfigHandler.GenerateClientConfig(contextMod, fileName);
|
||||
if (result.Success != true)
|
||||
{
|
||||
await UpdateFunc(true, result.Msg);
|
||||
|
|
@ -88,7 +96,7 @@ public class CoreManager
|
|||
await WindowsUtils.RemoveTunDevice();
|
||||
}
|
||||
|
||||
await CoreStart(mainContext);
|
||||
await CoreStart(contextMod);
|
||||
await CoreStartPreService(preContext);
|
||||
if (_processService != null)
|
||||
{
|
||||
|
|
@ -98,7 +106,7 @@ public class CoreManager
|
|||
|
||||
public async Task<ProcessService?> LoadCoreConfigSpeedtest(List<ServerTestItem> selecteds)
|
||||
{
|
||||
var coreType = selecteds.FirstOrDefault()?.CoreType == ECoreType.sing_box ? ECoreType.sing_box : ECoreType.Xray;
|
||||
var coreType = selecteds.Any(t => Global.SingboxOnlyConfigType.Contains(t.ConfigType)) ? ECoreType.sing_box : ECoreType.Xray;
|
||||
var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false));
|
||||
var configPath = Utils.GetBinConfigPath(fileName);
|
||||
var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, configPath, selecteds, coreType);
|
||||
|
|
|
|||
|
|
@ -52,10 +52,10 @@ public class GroupProfileManager
|
|||
return false;
|
||||
}
|
||||
|
||||
var childItems = await AppManager.Instance.GetProfileItemsByIndexIds(childIds);
|
||||
foreach (var childItem in childItems)
|
||||
foreach (var child in childIds)
|
||||
{
|
||||
if (await HasCycle(childItem.IndexId, childItem?.GetProtocolExtra(), visited, stack))
|
||||
var childItem = await AppManager.Instance.GetProfileItem(child);
|
||||
if (await HasCycle(child, childItem?.GetProtocolExtra(), visited, stack))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
|
@ -103,7 +103,26 @@ public class GroupProfileManager
|
|||
return [];
|
||||
}
|
||||
|
||||
var ordered = await AppManager.Instance.GetProfileItemsOrderedByIndexIds(childProfileIds);
|
||||
var childProfiles = await AppManager.Instance.GetProfileItemsByIndexIds(childProfileIds);
|
||||
if (childProfiles == null || childProfiles.Count == 0)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
var profileMap = childProfiles
|
||||
.Where(p => p != null && !p.IndexId.IsNullOrEmpty())
|
||||
.GroupBy(p => p!.IndexId!)
|
||||
.ToDictionary(g => g.Key, g => g.First());
|
||||
|
||||
var ordered = new List<ProfileItem>(childProfileIds.Count);
|
||||
foreach (var id in childProfileIds)
|
||||
{
|
||||
if (id != null && profileMap.TryGetValue(id, out var item) && item != null)
|
||||
{
|
||||
ordered.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
return ordered;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,25 +38,4 @@ public class NoticeManager
|
|||
Enqueue(msg);
|
||||
SendMessage(msg);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends each error and warning in <paramref name="validatorResult"/> to the message panel
|
||||
/// and enqueues a summary snack notification (capped at 10 messages).
|
||||
/// Returns <c>true</c> when there were any messages so the caller can decide on early-return
|
||||
/// based on <see cref="NodeValidatorResult.Success"/>.
|
||||
/// </summary>
|
||||
public bool NotifyValidatorResult(NodeValidatorResult validatorResult)
|
||||
{
|
||||
var msgs = new List<string>([.. validatorResult.Errors, .. validatorResult.Warnings]);
|
||||
if (msgs.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
foreach (var msg in msgs)
|
||||
{
|
||||
SendMessage(msg);
|
||||
}
|
||||
Enqueue(Utils.List2String(msgs.Take(10).ToList(), true));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,4 @@ public class ServerTestItem
|
|||
public EConfigType ConfigType { get; set; }
|
||||
public bool AllowTest { get; set; }
|
||||
public int QueueNum { get; set; }
|
||||
public required ProfileItem Profile { get; set; }
|
||||
public ECoreType CoreType { get; set; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -222,6 +222,12 @@ public class DnsServer4Ray
|
|||
public List<string>? domains { get; set; }
|
||||
public bool? skipFallback { get; set; }
|
||||
public List<string>? expectedIPs { get; set; }
|
||||
public List<string>? unexpectedIPs { get; set; }
|
||||
public string? clientIp { get; set; }
|
||||
public string? queryStrategy { get; set; }
|
||||
public int? timeoutMs { get; set; }
|
||||
public bool? disableCache { get; set; }
|
||||
public bool? finalQuery { get; set; }
|
||||
public string? tag { get; set; }
|
||||
}
|
||||
|
||||
|
|
|
|||
75
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
75
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
|
|
@ -843,15 +843,6 @@ namespace ServiceLib.Resx {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 All configurations 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuAllServers {
|
||||
get {
|
||||
return ResourceManager.GetString("menuAllServers", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Backup and Restore 的本地化字符串。
|
||||
/// </summary>
|
||||
|
|
@ -1024,20 +1015,74 @@ namespace ServiceLib.Resx {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Generate Policy Group 的本地化字符串。
|
||||
/// 查找类似 Generate Policy Group from Multiple Profiles 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuGenGroupServer {
|
||||
public static string menuGenGroupMultipleServer {
|
||||
get {
|
||||
return ResourceManager.GetString("menuGenGroupServer", resourceCulture);
|
||||
return ResourceManager.GetString("menuGenGroupMultipleServer", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Group by Region 的本地化字符串。
|
||||
/// 查找类似 Fallback by sing-box 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuGenRegionGroup {
|
||||
public static string menuGenGroupMultipleServerSingBoxFallback {
|
||||
get {
|
||||
return ResourceManager.GetString("menuGenRegionGroup", resourceCulture);
|
||||
return ResourceManager.GetString("menuGenGroupMultipleServerSingBoxFallback", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 LeastPing by sing-box 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuGenGroupMultipleServerSingBoxLeastPing {
|
||||
get {
|
||||
return ResourceManager.GetString("menuGenGroupMultipleServerSingBoxLeastPing", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Fallback by Xray 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuGenGroupMultipleServerXrayFallback {
|
||||
get {
|
||||
return ResourceManager.GetString("menuGenGroupMultipleServerXrayFallback", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 LeastLoad by Xray 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuGenGroupMultipleServerXrayLeastLoad {
|
||||
get {
|
||||
return ResourceManager.GetString("menuGenGroupMultipleServerXrayLeastLoad", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 LeastPing by Xray 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuGenGroupMultipleServerXrayLeastPing {
|
||||
get {
|
||||
return ResourceManager.GetString("menuGenGroupMultipleServerXrayLeastPing", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Random by Xray 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuGenGroupMultipleServerXrayRandom {
|
||||
get {
|
||||
return ResourceManager.GetString("menuGenGroupMultipleServerXrayRandom", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 RoundRobin by Xray 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuGenGroupMultipleServerXrayRoundRobin {
|
||||
get {
|
||||
return ResourceManager.GetString("menuGenGroupMultipleServerXrayRoundRobin", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1371,6 +1371,24 @@
|
|||
<data name="TbPorts7Tips" xml:space="preserve">
|
||||
<value>مخفی و پورت می شود، با کاما (،) جدا می شود</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServer" xml:space="preserve">
|
||||
<value>Generate Policy Group from Multiple Profiles</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerXrayRandom" xml:space="preserve">
|
||||
<value>چند سرور تصادفی توسط Xray</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerXrayRoundRobin" xml:space="preserve">
|
||||
<value>چند سرور RoundRobin توسط Xray</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerXrayLeastPing" xml:space="preserve">
|
||||
<value>چند سرور LeastPing توسط Xray</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerXrayLeastLoad" xml:space="preserve">
|
||||
<value>چند سرور LeastLoad توسط Xray</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerSingBoxLeastPing" xml:space="preserve">
|
||||
<value>LeastPing چند سرور توسط sing-box</value>
|
||||
</data>
|
||||
<data name="menuExportConfig" xml:space="preserve">
|
||||
<value>صادر کردن سرور</value>
|
||||
</data>
|
||||
|
|
@ -1515,6 +1533,12 @@
|
|||
<data name="TbFallback" xml:space="preserve">
|
||||
<value>Fallback</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
|
||||
<value>Multi-Configuration Fallback by sing-box</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
|
||||
<value>Multi-Configuration Fallback by Xray</value>
|
||||
</data>
|
||||
<data name="MsgCoreNotSupportNetwork" xml:space="preserve">
|
||||
<value>Core '{0}' does not support network type '{1}'</value>
|
||||
</data>
|
||||
|
|
@ -1662,15 +1686,6 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
|||
<data name="MsgSubscriptionNextProfileNotFound" xml:space="preserve">
|
||||
<value>Subscription next proxy {0} not found. Skipping.</value>
|
||||
</data>
|
||||
<data name="menuGenGroupServer" xml:space="preserve">
|
||||
<value>Generate Policy Group</value>
|
||||
</data>
|
||||
<data name="menuAllServers" xml:space="preserve">
|
||||
<value>All configurations</value>
|
||||
</data>
|
||||
<data name="menuGenRegionGroup" xml:space="preserve">
|
||||
<value>Group by Region</value>
|
||||
</data>
|
||||
<data name="TbUot" xml:space="preserve">
|
||||
<value>UDP over TCP</value>
|
||||
</data>
|
||||
|
|
|
|||
|
|
@ -1368,6 +1368,24 @@
|
|||
<data name="TbPorts7Tips" xml:space="preserve">
|
||||
<value>Écrase le port ; pour plusieurs groupes, séparer par virgules (,)</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServer" xml:space="preserve">
|
||||
<value>Générer un groupe de stratégie depuis plusieurs profils</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerXrayRandom" xml:space="preserve">
|
||||
<value>Xray aléatoire (multi-sélection)</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerXrayRoundRobin" xml:space="preserve">
|
||||
<value>Xray équilibrage (tourniquet) multi-sélection</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerXrayLeastPing" xml:space="preserve">
|
||||
<value>Xray latence minimale (multi-sélection)</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerXrayLeastLoad" xml:space="preserve">
|
||||
<value>Xray le plus stable (multi-sélection)</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerSingBoxLeastPing" xml:space="preserve">
|
||||
<value>sing-box latence minimale (multi-sélection)</value>
|
||||
</data>
|
||||
<data name="menuExportConfig" xml:space="preserve">
|
||||
<value>Exporter</value>
|
||||
</data>
|
||||
|
|
@ -1512,6 +1530,12 @@
|
|||
<data name="TbFallback" xml:space="preserve">
|
||||
<value>Basculement (failover)</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
|
||||
<value>sing-box basculement (multi-sélection)</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
|
||||
<value>Xray basculement (multi-sélection)</value>
|
||||
</data>
|
||||
<data name="MsgCoreNotSupportNetwork" xml:space="preserve">
|
||||
<value>Le cœur « {0} » ne prend pas en charge le type de réseau « {1} »</value>
|
||||
</data>
|
||||
|
|
@ -1659,15 +1683,6 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
|||
<data name="MsgSubscriptionNextProfileNotFound" xml:space="preserve">
|
||||
<value>Subscription next proxy {0} not found. Skipping.</value>
|
||||
</data>
|
||||
<data name="menuGenGroupServer" xml:space="preserve">
|
||||
<value>Generate Policy Group</value>
|
||||
</data>
|
||||
<data name="menuAllServers" xml:space="preserve">
|
||||
<value>All configurations</value>
|
||||
</data>
|
||||
<data name="menuGenRegionGroup" xml:space="preserve">
|
||||
<value>Group by Region</value>
|
||||
</data>
|
||||
<data name="TbUot" xml:space="preserve">
|
||||
<value>UDP over TCP</value>
|
||||
</data>
|
||||
|
|
|
|||
|
|
@ -1371,6 +1371,24 @@
|
|||
<data name="TbPorts7Tips" xml:space="preserve">
|
||||
<value>A portot lefedi, vesszővel (,) elválasztva</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServer" xml:space="preserve">
|
||||
<value>Generate Policy Group from Multiple Profiles</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerXrayRandom" xml:space="preserve">
|
||||
<value>Több konfiguráció véletlenszerűen Xray szerint</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerXrayRoundRobin" xml:space="preserve">
|
||||
<value>Több konfiguráció RoundRobin Xray szerint</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerXrayLeastPing" xml:space="preserve">
|
||||
<value>Több konfiguráció legkisebb pinggel Xray szerint</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerXrayLeastLoad" xml:space="preserve">
|
||||
<value>Több konfiguráció legkisebb terheléssel Xray szerint</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerSingBoxLeastPing" xml:space="preserve">
|
||||
<value>Több konfiguráció legkisebb pinggel sing-box szerint</value>
|
||||
</data>
|
||||
<data name="menuExportConfig" xml:space="preserve">
|
||||
<value>Konfiguráció exportálása</value>
|
||||
</data>
|
||||
|
|
@ -1515,6 +1533,12 @@
|
|||
<data name="TbFallback" xml:space="preserve">
|
||||
<value>Fallback</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
|
||||
<value>Multi-Configuration Fallback by sing-box</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
|
||||
<value>Multi-Configuration Fallback by Xray</value>
|
||||
</data>
|
||||
<data name="MsgCoreNotSupportNetwork" xml:space="preserve">
|
||||
<value>Core '{0}' does not support network type '{1}'</value>
|
||||
</data>
|
||||
|
|
@ -1662,15 +1686,6 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
|||
<data name="MsgSubscriptionNextProfileNotFound" xml:space="preserve">
|
||||
<value>Subscription next proxy {0} not found. Skipping.</value>
|
||||
</data>
|
||||
<data name="menuGenGroupServer" xml:space="preserve">
|
||||
<value>Generate Policy Group</value>
|
||||
</data>
|
||||
<data name="menuAllServers" xml:space="preserve">
|
||||
<value>All configurations</value>
|
||||
</data>
|
||||
<data name="menuGenRegionGroup" xml:space="preserve">
|
||||
<value>Group by Region</value>
|
||||
</data>
|
||||
<data name="TbUot" xml:space="preserve">
|
||||
<value>UDP over TCP</value>
|
||||
</data>
|
||||
|
|
|
|||
|
|
@ -1371,6 +1371,24 @@
|
|||
<data name="TbPorts7Tips" xml:space="preserve">
|
||||
<value>Will cover the port, separate with commas (,)</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServer" xml:space="preserve">
|
||||
<value>Generate Policy Group from Multiple Profiles</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerXrayRandom" xml:space="preserve">
|
||||
<value>Random by Xray</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerXrayRoundRobin" xml:space="preserve">
|
||||
<value>RoundRobin by Xray</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerXrayLeastPing" xml:space="preserve">
|
||||
<value>LeastPing by Xray</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerXrayLeastLoad" xml:space="preserve">
|
||||
<value>LeastLoad by Xray</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerSingBoxLeastPing" xml:space="preserve">
|
||||
<value>LeastPing by sing-box</value>
|
||||
</data>
|
||||
<data name="menuExportConfig" xml:space="preserve">
|
||||
<value>Export</value>
|
||||
</data>
|
||||
|
|
@ -1515,6 +1533,12 @@
|
|||
<data name="TbFallback" xml:space="preserve">
|
||||
<value>Fallback</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
|
||||
<value>Fallback by sing-box</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
|
||||
<value>Fallback by Xray</value>
|
||||
</data>
|
||||
<data name="MsgCoreNotSupportNetwork" xml:space="preserve">
|
||||
<value>Core '{0}' does not support network type '{1}'</value>
|
||||
</data>
|
||||
|
|
@ -1662,15 +1686,6 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
|||
<data name="MsgSubscriptionNextProfileNotFound" xml:space="preserve">
|
||||
<value>Subscription next proxy {0} not found. Skipping.</value>
|
||||
</data>
|
||||
<data name="menuGenGroupServer" xml:space="preserve">
|
||||
<value>Generate Policy Group</value>
|
||||
</data>
|
||||
<data name="menuAllServers" xml:space="preserve">
|
||||
<value>All configurations</value>
|
||||
</data>
|
||||
<data name="menuGenRegionGroup" xml:space="preserve">
|
||||
<value>Group by Region</value>
|
||||
</data>
|
||||
<data name="TbUot" xml:space="preserve">
|
||||
<value>UDP over TCP</value>
|
||||
</data>
|
||||
|
|
|
|||
|
|
@ -1371,6 +1371,24 @@
|
|||
<data name="TbPorts7Tips" xml:space="preserve">
|
||||
<value>Заменит указанный порт, перечисляйте через запятую (,)</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServer" xml:space="preserve">
|
||||
<value>Generate Policy Group from Multiple Profiles</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerXrayRandom" xml:space="preserve">
|
||||
<value>Случайный (Xray)</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerXrayRoundRobin" xml:space="preserve">
|
||||
<value>Круговой (Xray)</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerXrayLeastPing" xml:space="preserve">
|
||||
<value>Минимальное RTT (минимальное время туда-обратно) (Xray)</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerXrayLeastLoad" xml:space="preserve">
|
||||
<value>Минимальная нагрузка (Xray)</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerSingBoxLeastPing" xml:space="preserve">
|
||||
<value>Минимальное RTT (минимальное время туда-обратно) (sing-box)</value>
|
||||
</data>
|
||||
<data name="menuExportConfig" xml:space="preserve">
|
||||
<value>Экспортировать конфигурацию</value>
|
||||
</data>
|
||||
|
|
@ -1515,6 +1533,12 @@
|
|||
<data name="TbFallback" xml:space="preserve">
|
||||
<value>Fallback</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
|
||||
<value>Multi-Configuration Fallback by sing-box</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
|
||||
<value>Multi-Configuration Fallback by Xray</value>
|
||||
</data>
|
||||
<data name="MsgCoreNotSupportNetwork" xml:space="preserve">
|
||||
<value>Core '{0}' does not support network type '{1}'</value>
|
||||
</data>
|
||||
|
|
@ -1662,15 +1686,6 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
|||
<data name="MsgSubscriptionNextProfileNotFound" xml:space="preserve">
|
||||
<value>Subscription next proxy {0} not found. Skipping.</value>
|
||||
</data>
|
||||
<data name="menuGenGroupServer" xml:space="preserve">
|
||||
<value>Generate Policy Group</value>
|
||||
</data>
|
||||
<data name="menuAllServers" xml:space="preserve">
|
||||
<value>All configurations</value>
|
||||
</data>
|
||||
<data name="menuGenRegionGroup" xml:space="preserve">
|
||||
<value>Group by Region</value>
|
||||
</data>
|
||||
<data name="TbUot" xml:space="preserve">
|
||||
<value>UDP over TCP</value>
|
||||
</data>
|
||||
|
|
|
|||
|
|
@ -1368,6 +1368,24 @@
|
|||
<data name="TbPorts7Tips" xml:space="preserve">
|
||||
<value>会覆盖端口,多组时用逗号 (,) 隔开</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServer" xml:space="preserve">
|
||||
<value>多选生成策略组</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerXrayRandom" xml:space="preserve">
|
||||
<value>多选随机 Xray</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerXrayRoundRobin" xml:space="preserve">
|
||||
<value>多选负载均衡 Xray</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerXrayLeastPing" xml:space="preserve">
|
||||
<value>多选最低延迟 Xray</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerXrayLeastLoad" xml:space="preserve">
|
||||
<value>多选最稳定 Xray</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerSingBoxLeastPing" xml:space="preserve">
|
||||
<value>多选最低延迟 sing-box</value>
|
||||
</data>
|
||||
<data name="menuExportConfig" xml:space="preserve">
|
||||
<value>导出</value>
|
||||
</data>
|
||||
|
|
@ -1512,6 +1530,12 @@
|
|||
<data name="TbFallback" xml:space="preserve">
|
||||
<value>故障转移</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
|
||||
<value>多选故障转移 sing-box</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
|
||||
<value>多选故障转移 Xray</value>
|
||||
</data>
|
||||
<data name="MsgCoreNotSupportNetwork" xml:space="preserve">
|
||||
<value>核心 '{0}' 不支持网络类型 '{1}'</value>
|
||||
</data>
|
||||
|
|
@ -1659,15 +1683,6 @@
|
|||
<data name="MsgSubscriptionNextProfileNotFound" xml:space="preserve">
|
||||
<value>订阅后置节点 {0} 未找到,已跳过。</value>
|
||||
</data>
|
||||
<data name="menuGenGroupServer" xml:space="preserve">
|
||||
<value>一键生成策略组</value>
|
||||
</data>
|
||||
<data name="menuAllServers" xml:space="preserve">
|
||||
<value>全部配置项</value>
|
||||
</data>
|
||||
<data name="menuGenRegionGroup" xml:space="preserve">
|
||||
<value>按地区分组</value>
|
||||
</data>
|
||||
<data name="TbUot" xml:space="preserve">
|
||||
<value>UDP over TCP</value>
|
||||
</data>
|
||||
|
|
|
|||
|
|
@ -1368,6 +1368,24 @@
|
|||
<data name="TbPorts7Tips" xml:space="preserve">
|
||||
<value>會覆蓋埠,多組時用逗號 (,) 隔開</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServer" xml:space="preserve">
|
||||
<value>多選生成策略組</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerXrayRandom" xml:space="preserve">
|
||||
<value>多選隨機 Xray</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerXrayRoundRobin" xml:space="preserve">
|
||||
<value>多選負載平衡 Xray</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerXrayLeastPing" xml:space="preserve">
|
||||
<value>多選最低延遲 Xray</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerXrayLeastLoad" xml:space="preserve">
|
||||
<value>多選最穩定 Xray</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerSingBoxLeastPing" xml:space="preserve">
|
||||
<value>多選最低延遲 sing-box</value>
|
||||
</data>
|
||||
<data name="menuExportConfig" xml:space="preserve">
|
||||
<value>匯出</value>
|
||||
</data>
|
||||
|
|
@ -1512,6 +1530,12 @@
|
|||
<data name="TbFallback" xml:space="preserve">
|
||||
<value>容錯移轉</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
|
||||
<value>多選容錯移轉 sing-box</value>
|
||||
</data>
|
||||
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
|
||||
<value>多選容錯移轉 Xray</value>
|
||||
</data>
|
||||
<data name="MsgCoreNotSupportNetwork" xml:space="preserve">
|
||||
<value>核心 '{0}' 不支援網路類型 '{1}'</value>
|
||||
</data>
|
||||
|
|
@ -1659,15 +1683,6 @@
|
|||
<data name="MsgSubscriptionNextProfileNotFound" xml:space="preserve">
|
||||
<value>Subscription next proxy {0} not found. Skipping.</value>
|
||||
</data>
|
||||
<data name="menuGenGroupServer" xml:space="preserve">
|
||||
<value>Generate Policy Group</value>
|
||||
</data>
|
||||
<data name="menuAllServers" xml:space="preserve">
|
||||
<value>All configurations</value>
|
||||
</data>
|
||||
<data name="menuGenRegionGroup" xml:space="preserve">
|
||||
<value>Group by Region</value>
|
||||
</data>
|
||||
<data name="TbUot" xml:space="preserve">
|
||||
<value>UDP over TCP</value>
|
||||
</data>
|
||||
|
|
|
|||
|
|
@ -149,11 +149,11 @@ public partial class CoreConfigSingboxService(CoreConfigContext context)
|
|||
|
||||
foreach (var it in selecteds)
|
||||
{
|
||||
if (!(Global.SingboxSupportConfigType.Contains(it.ConfigType) || it.ConfigType.IsGroupType()))
|
||||
if (!Global.SingboxSupportConfigType.Contains(it.ConfigType))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!it.ConfigType.IsComplexType() && it.Port <= 0)
|
||||
if (it.Port <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -148,20 +148,15 @@ public partial class CoreConfigSingboxService
|
|||
_coreConfig.dns ??= new Dns4Sbox();
|
||||
_coreConfig.dns.rules ??= [];
|
||||
|
||||
_coreConfig.dns.rules.Add(new() { ip_accept_any = true, server = Global.SingboxHostsDNSTag });
|
||||
|
||||
if (context.ProtectDomainList.Count > 0)
|
||||
_coreConfig.dns.rules.AddRange(new[]
|
||||
{
|
||||
_coreConfig.dns.rules.Add(new()
|
||||
new Rule4Sbox { ip_accept_any = true, server = Global.SingboxHostsDNSTag },
|
||||
new Rule4Sbox
|
||||
{
|
||||
server = Global.SingboxDirectDNSTag,
|
||||
strategy = Utils.DomainStrategy4Sbox(simpleDnsItem.Strategy4Freedom),
|
||||
domain = context.ProtectDomainList.ToList(),
|
||||
});
|
||||
}
|
||||
|
||||
_coreConfig.dns.rules.AddRange(new[]
|
||||
{
|
||||
},
|
||||
new Rule4Sbox
|
||||
{
|
||||
server = Global.SingboxRemoteDNSTag,
|
||||
|
|
|
|||
|
|
@ -112,11 +112,11 @@ public partial class CoreConfigV2rayService(CoreConfigContext context)
|
|||
|
||||
foreach (var it in selecteds)
|
||||
{
|
||||
if (!(Global.XraySupportConfigType.Contains(it.ConfigType) || it.ConfigType.IsGroupType()))
|
||||
if (!Global.XraySupportConfigType.Contains(it.ConfigType))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!it.ConfigType.IsComplexType() && it.Port <= 0)
|
||||
if (it.Port <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
@ -180,13 +180,13 @@ public partial class CoreConfigV2rayService(CoreConfigContext context)
|
|||
//rule
|
||||
RulesItem4Ray rule = new()
|
||||
{
|
||||
inboundTag = [inbound.tag],
|
||||
inboundTag = new List<string> { inbound.tag },
|
||||
outboundTag = tag,
|
||||
type = "field"
|
||||
};
|
||||
if (isBalancer)
|
||||
{
|
||||
rule.balancerTag = tag + Global.BalancerTagSuffix;
|
||||
rule.balancerTag = tag;
|
||||
rule.outboundTag = null;
|
||||
}
|
||||
_coreConfig.routing.rules.Add(rule);
|
||||
|
|
|
|||
|
|
@ -71,25 +71,6 @@ public partial class CoreConfigV2rayService
|
|||
dnsItem.enableParallelQuery = simpleDnsItem?.ParallelQuery is true ? true : null;
|
||||
|
||||
// DNS routing
|
||||
var directDnsTags = dnsItem.servers
|
||||
.Select(server =>
|
||||
{
|
||||
var tagNode = (server as JsonObject)?["tag"];
|
||||
return tagNode is JsonValue value && value.TryGetValue<string>(out var tag) ? tag : null;
|
||||
})
|
||||
.Where(tag => tag is not null && tag.StartsWith(Global.DirectDnsTag, StringComparison.Ordinal))
|
||||
.Select(tag => tag!)
|
||||
.ToList();
|
||||
if (directDnsTags.Count > 0)
|
||||
{
|
||||
_coreConfig.routing.rules.Add(new()
|
||||
{
|
||||
type = "field",
|
||||
inboundTag = directDnsTags,
|
||||
outboundTag = Global.DirectTag,
|
||||
});
|
||||
}
|
||||
|
||||
var finalRule = BuildFinalRule();
|
||||
dnsItem.tag = Global.DnsTag;
|
||||
_coreConfig.routing.rules.Add(new()
|
||||
|
|
@ -97,7 +78,7 @@ public partial class CoreConfigV2rayService
|
|||
type = "field",
|
||||
inboundTag = [Global.DnsTag],
|
||||
outboundTag = finalRule.outboundTag,
|
||||
balancerTag = finalRule.balancerTag,
|
||||
balancerTag = finalRule.balancerTag
|
||||
});
|
||||
|
||||
_coreConfig.dns = dnsItem;
|
||||
|
|
@ -231,13 +212,11 @@ public partial class CoreConfigV2rayService
|
|||
|
||||
dnsItem.servers ??= [];
|
||||
|
||||
var directDnsTagIndex = 1;
|
||||
|
||||
AddDnsServers(remoteDNSAddress, proxyDomainList);
|
||||
AddDnsServers(directDNSAddress, directDomainList, true);
|
||||
AddDnsServers(directDNSAddress, directDomainList);
|
||||
AddDnsServers(remoteDNSAddress, proxyGeositeList);
|
||||
AddDnsServers(directDNSAddress, directGeositeList, true);
|
||||
AddDnsServers(directDNSAddress, expectedDomainList, true, expectedIPs);
|
||||
AddDnsServers(directDNSAddress, directGeositeList);
|
||||
AddDnsServers(directDNSAddress, expectedDomainList, expectedIPs);
|
||||
if (dnsServerDomains.Count > 0)
|
||||
{
|
||||
AddDnsServers(bootstrapDNSAddress, dnsServerDomains);
|
||||
|
|
@ -255,21 +234,8 @@ public partial class CoreConfigV2rayService
|
|||
useDirectDns = noDomain && noProcess && isAnyIp && isAnyPort && isAnyNetwork;
|
||||
}
|
||||
|
||||
if (!useDirectDns)
|
||||
{
|
||||
dnsItem.servers.AddRange(remoteDNSAddress);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var dns in directDNSAddress)
|
||||
{
|
||||
var dnsServer = CreateDnsServer(dns, []);
|
||||
dnsServer.tag = $"{Global.DirectDnsTag}-{directDnsTagIndex++}";
|
||||
dnsServer.skipFallback = false;
|
||||
dnsItem.servers.Add(JsonUtils.SerializeToNode(dnsServer,
|
||||
new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }));
|
||||
}
|
||||
}
|
||||
var defaultDnsServers = useDirectDns ? directDNSAddress : remoteDNSAddress;
|
||||
dnsItem.servers.AddRange(defaultDnsServers);
|
||||
return;
|
||||
|
||||
static List<string> ParseDnsAddresses(string? dnsInput, string defaultAddress)
|
||||
|
|
@ -283,7 +249,7 @@ public partial class CoreConfigV2rayService
|
|||
return addresses.Count > 0 ? addresses : new List<string> { defaultAddress };
|
||||
}
|
||||
|
||||
static DnsServer4Ray CreateDnsServer(string dnsAddress, List<string> domains, List<string>? expectedIPs = null)
|
||||
static object? CreateDnsServer(string dnsAddress, List<string> domains, List<string>? expectedIPs = null)
|
||||
{
|
||||
var (domain, scheme, port, path) = Utils.ParseUrl(dnsAddress);
|
||||
var domainFinal = dnsAddress;
|
||||
|
|
@ -306,10 +272,13 @@ public partial class CoreConfigV2rayService
|
|||
domains = domains.Count > 0 ? domains : null,
|
||||
expectedIPs = expectedIPs?.Count > 0 ? expectedIPs : null
|
||||
};
|
||||
return dnsServer;
|
||||
return JsonUtils.SerializeToNode(dnsServer, new JsonSerializerOptions
|
||||
{
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
|
||||
});
|
||||
}
|
||||
|
||||
void AddDnsServers(List<string> dnsAddresses, List<string> domains, bool isDirectDns = false, List<string>? expectedIPs = null)
|
||||
void AddDnsServers(List<string> dnsAddresses, List<string> domains, List<string>? expectedIPs = null)
|
||||
{
|
||||
if (domains.Count <= 0)
|
||||
{
|
||||
|
|
@ -317,14 +286,7 @@ public partial class CoreConfigV2rayService
|
|||
}
|
||||
foreach (var dnsAddress in dnsAddresses)
|
||||
{
|
||||
var dnsServer = CreateDnsServer(dnsAddress, domains, expectedIPs);
|
||||
if (isDirectDns)
|
||||
{
|
||||
dnsServer.tag = $"{Global.DirectDnsTag}-{directDnsTagIndex++}";
|
||||
}
|
||||
var dnsServerNode = JsonUtils.SerializeToNode(dnsServer,
|
||||
new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull });
|
||||
dnsItem.servers.Add(dnsServerNode);
|
||||
dnsItem.servers.Add(CreateDnsServer(dnsAddress, domains, expectedIPs));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,36 +61,26 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
|
|||
|
||||
private async Task<List<ServerTestItem>> GetClearItem(ESpeedActionType actionType, List<ProfileItem> selecteds)
|
||||
{
|
||||
var lstSelected = new List<ServerTestItem>(selecteds.Count);
|
||||
var ids = selecteds.Where(it => !it.IndexId.IsNullOrEmpty()
|
||||
&& it.ConfigType != EConfigType.Custom
|
||||
&& (it.ConfigType.IsComplexType() || it.Port > 0))
|
||||
.Select(it => it.IndexId)
|
||||
.ToList();
|
||||
var profileMap = await AppManager.Instance.GetProfileItemsByIndexIdsAsMap(ids);
|
||||
for (var i = 0; i < selecteds.Count; i++)
|
||||
var lstSelected = new List<ServerTestItem>();
|
||||
foreach (var it in selecteds)
|
||||
{
|
||||
var it = selecteds[i];
|
||||
if (it.ConfigType == EConfigType.Custom)
|
||||
if (it.ConfigType.IsComplexType())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!it.ConfigType.IsComplexType() && it.Port <= 0)
|
||||
if (it.Port <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var profile = profileMap.GetValueOrDefault(it.IndexId, it);
|
||||
lstSelected.Add(new ServerTestItem()
|
||||
{
|
||||
IndexId = it.IndexId,
|
||||
Address = it.Address,
|
||||
Port = it.Port,
|
||||
ConfigType = it.ConfigType,
|
||||
QueueNum = i,
|
||||
Profile = profile,
|
||||
CoreType = AppManager.Instance.GetCoreType(profile, it.ConfigType),
|
||||
QueueNum = selecteds.IndexOf(it)
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -363,8 +353,8 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
|
|||
private List<List<ServerTestItem>> GetTestBatchItem(List<ServerTestItem> lstSelected, int pageSize)
|
||||
{
|
||||
List<List<ServerTestItem>> lstTest = new();
|
||||
var lst1 = lstSelected.Where(t => t.CoreType == ECoreType.Xray).ToList();
|
||||
var lst2 = lstSelected.Where(t => t.CoreType == ECoreType.sing_box).ToList();
|
||||
var lst1 = lstSelected.Where(t => Global.XraySupportConfigType.Contains(t.ConfigType)).ToList();
|
||||
var lst2 = lstSelected.Where(t => Global.SingboxOnlyConfigType.Contains(t.ConfigType)).ToList();
|
||||
|
||||
for (var num = 0; num < (int)Math.Ceiling(lst1.Count * 1.0 / pageSize); num++)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -99,8 +99,15 @@ public class AddGroupServerViewModel : MyReactiveObject
|
|||
Filter = protocolExtra?.Filter;
|
||||
|
||||
var childIndexIds = Utils.String2List(protocolExtra?.ChildItems) ?? [];
|
||||
var childItemList = await AppManager.Instance.GetProfileItemsOrderedByIndexIds(childIndexIds);
|
||||
ChildItemsObs.AddRange(childItemList);
|
||||
foreach (var item in childIndexIds)
|
||||
{
|
||||
var child = await AppManager.Instance.GetProfileItem(item);
|
||||
if (child == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
ChildItemsObs.Add(child);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ChildRemoveAsync()
|
||||
|
|
|
|||
|
|
@ -551,15 +551,24 @@ public class MainWindowViewModel : MyReactiveObject
|
|||
NoticeManager.Instance.Enqueue(ResUI.CheckServerSettings);
|
||||
return;
|
||||
}
|
||||
var allResult = await CoreConfigContextBuilder.BuildAll(_config, profileItem);
|
||||
if (NoticeManager.Instance.NotifyValidatorResult(allResult.CombinedValidatorResult) && !allResult.Success)
|
||||
var (context, validatorResult) = await CoreConfigContextBuilder.Build(_config, profileItem);
|
||||
var msgs = new List<string>([..validatorResult.Errors, ..validatorResult.Warnings]);
|
||||
if (msgs.Count > 0)
|
||||
{
|
||||
return;
|
||||
foreach (var msg in msgs)
|
||||
{
|
||||
NoticeManager.Instance.SendMessage(msg);
|
||||
}
|
||||
NoticeManager.Instance.Enqueue(Utils.List2String(msgs.Take(10).ToList(), true));
|
||||
if (!validatorResult.Success)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
await LoadCore(allResult.ResolvedMainContext, allResult.PreSocksResult?.Context);
|
||||
await LoadCore(context);
|
||||
await SysProxyHandler.UpdateSysProxy(_config, false);
|
||||
await Task.Delay(1000);
|
||||
});
|
||||
|
|
@ -600,9 +609,9 @@ public class MainWindowViewModel : MyReactiveObject
|
|||
RxApp.MainThreadScheduler.Schedule(() => BlReloadEnabled = enabled);
|
||||
}
|
||||
|
||||
private async Task LoadCore(CoreConfigContext? mainContext, CoreConfigContext? preContext)
|
||||
private async Task LoadCore(CoreConfigContext? context)
|
||||
{
|
||||
await CoreManager.Instance.LoadCore(mainContext, preContext);
|
||||
await CoreManager.Instance.LoadCore(context);
|
||||
}
|
||||
|
||||
#endregion core job
|
||||
|
|
|
|||
|
|
@ -255,7 +255,19 @@ public class ProfilesSelectViewModel : MyReactiveObject
|
|||
{
|
||||
return null;
|
||||
}
|
||||
var lst = await AppManager.Instance.GetProfileItemsOrderedByIndexIds(SelectedProfiles.Select(sp => sp?.IndexId));
|
||||
var lst = new List<ProfileItem>();
|
||||
foreach (var sp in SelectedProfiles)
|
||||
{
|
||||
if (string.IsNullOrEmpty(sp?.IndexId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var item = await AppManager.Instance.GetProfileItem(sp.IndexId);
|
||||
if (item != null)
|
||||
{
|
||||
lst.Add(item);
|
||||
}
|
||||
}
|
||||
if (lst.Count == 0)
|
||||
{
|
||||
NoticeManager.Instance.Enqueue(ResUI.PleaseSelectServer);
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ public class ProfilesViewModel : MyReactiveObject
|
|||
private string _serverFilter = string.Empty;
|
||||
private Dictionary<string, bool> _dicHeaderSort = new();
|
||||
private SpeedtestService? _speedtestService;
|
||||
private string? _pendingSelectIndexId;
|
||||
|
||||
#endregion private prop
|
||||
|
||||
|
|
@ -44,8 +43,13 @@ public class ProfilesViewModel : MyReactiveObject
|
|||
public ReactiveCommand<Unit, Unit> CopyServerCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> SetDefaultServerCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> ShareServerCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> GenGroupAllServerCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> GenGroupRegionServerCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> GenGroupMultipleServerXrayRandomCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> GenGroupMultipleServerXrayRoundRobinCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> GenGroupMultipleServerXrayLeastPingCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> GenGroupMultipleServerXrayLeastLoadCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> GenGroupMultipleServerXrayFallbackCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> GenGroupMultipleServerSingBoxLeastPingCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> GenGroupMultipleServerSingBoxFallbackCmd { get; }
|
||||
|
||||
//servers move
|
||||
public ReactiveCommand<Unit, Unit> MoveTopCmd { get; }
|
||||
|
|
@ -130,13 +134,33 @@ public class ProfilesViewModel : MyReactiveObject
|
|||
{
|
||||
await ShareServerAsync();
|
||||
}, canEditRemove);
|
||||
GenGroupAllServerCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
GenGroupMultipleServerXrayRandomCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await GenGroupAllServer();
|
||||
await GenGroupMultipleServer(ECoreType.Xray, EMultipleLoad.Random);
|
||||
}, canEditRemove);
|
||||
GenGroupRegionServerCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
GenGroupMultipleServerXrayRoundRobinCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await GenGroupRegionServer();
|
||||
await GenGroupMultipleServer(ECoreType.Xray, EMultipleLoad.RoundRobin);
|
||||
}, canEditRemove);
|
||||
GenGroupMultipleServerXrayLeastPingCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await GenGroupMultipleServer(ECoreType.Xray, EMultipleLoad.LeastPing);
|
||||
}, canEditRemove);
|
||||
GenGroupMultipleServerXrayLeastLoadCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await GenGroupMultipleServer(ECoreType.Xray, EMultipleLoad.LeastLoad);
|
||||
}, canEditRemove);
|
||||
GenGroupMultipleServerXrayFallbackCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await GenGroupMultipleServer(ECoreType.Xray, EMultipleLoad.Fallback);
|
||||
}, canEditRemove);
|
||||
GenGroupMultipleServerSingBoxLeastPingCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await GenGroupMultipleServer(ECoreType.sing_box, EMultipleLoad.LeastPing);
|
||||
}, canEditRemove);
|
||||
GenGroupMultipleServerSingBoxFallbackCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await GenGroupMultipleServer(ECoreType.sing_box, EMultipleLoad.Fallback);
|
||||
}, canEditRemove);
|
||||
|
||||
//servers move
|
||||
|
|
@ -368,14 +392,15 @@ public class ProfilesViewModel : MyReactiveObject
|
|||
ProfileItems.AddRange(lstModel);
|
||||
if (lstModel.Count > 0)
|
||||
{
|
||||
ProfileItemModel? selected = null;
|
||||
if (!_pendingSelectIndexId.IsNullOrEmpty())
|
||||
var selected = lstModel.FirstOrDefault(t => t.IndexId == _config.IndexId);
|
||||
if (selected != null)
|
||||
{
|
||||
selected = lstModel.FirstOrDefault(t => t.IndexId == _pendingSelectIndexId);
|
||||
_pendingSelectIndexId = null;
|
||||
SelectedProfile = selected;
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedProfile = lstModel.First();
|
||||
}
|
||||
selected ??= lstModel.FirstOrDefault(t => t.IndexId == _config.IndexId);
|
||||
SelectedProfile = selected ?? lstModel.First();
|
||||
}
|
||||
|
||||
await _updateView?.Invoke(EViewAction.DispatcherRefreshServersBiz, null);
|
||||
|
|
@ -456,7 +481,14 @@ public class ProfilesViewModel : MyReactiveObject
|
|||
var orderProfiles = SelectedProfiles?.OrderBy(t => t.Sort);
|
||||
if (latest)
|
||||
{
|
||||
lstSelected.AddRange(await AppManager.Instance.GetProfileItemsOrderedByIndexIds(orderProfiles.Select(sp => sp?.IndexId)));
|
||||
foreach (var profile in orderProfiles)
|
||||
{
|
||||
var item = await AppManager.Instance.GetProfileItem(profile.IndexId);
|
||||
if (item is not null)
|
||||
{
|
||||
lstSelected.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -609,29 +641,29 @@ public class ProfilesViewModel : MyReactiveObject
|
|||
await _updateView?.Invoke(EViewAction.ShareServer, url);
|
||||
}
|
||||
|
||||
private async Task GenGroupAllServer()
|
||||
private async Task GenGroupMultipleServer(ECoreType coreType, EMultipleLoad multipleLoad)
|
||||
{
|
||||
var ret = await ConfigHandler.AddGroupAllServer(_config, SelectedSub);
|
||||
if (ret.Success != true)
|
||||
var lstSelected = await GetProfileItems(true);
|
||||
if (lstSelected == null)
|
||||
{
|
||||
NoticeManager.Instance.Enqueue(ResUI.OperationFailed);
|
||||
return;
|
||||
}
|
||||
_pendingSelectIndexId = ret.Data?.ToString();
|
||||
await RefreshServers();
|
||||
}
|
||||
|
||||
private async Task GenGroupRegionServer()
|
||||
{
|
||||
var ret = await ConfigHandler.AddGroupRegionServer(_config, SelectedSub);
|
||||
var ret = await ConfigHandler.AddGroupServer4Multiple(_config, lstSelected, coreType, multipleLoad, SelectedSub?.Id);
|
||||
if (ret.Success != true)
|
||||
{
|
||||
NoticeManager.Instance.Enqueue(ResUI.OperationFailed);
|
||||
return;
|
||||
}
|
||||
var indexIdList = ret.Data as List<string>;
|
||||
_pendingSelectIndexId = indexIdList?.FirstOrDefault();
|
||||
await RefreshServers();
|
||||
if (ret?.Data?.ToString() == _config.IndexId)
|
||||
{
|
||||
await RefreshServers();
|
||||
Reload();
|
||||
}
|
||||
else
|
||||
{
|
||||
await SetDefaultServer(ret?.Data?.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SortServer(string colName)
|
||||
|
|
@ -757,9 +789,18 @@ public class ProfilesViewModel : MyReactiveObject
|
|||
}
|
||||
|
||||
var (context, validatorResult) = await CoreConfigContextBuilder.Build(_config, item);
|
||||
if (NoticeManager.Instance.NotifyValidatorResult(validatorResult) && !validatorResult.Success)
|
||||
var msgs = new List<string>([..validatorResult.Errors, ..validatorResult.Warnings]);
|
||||
if (msgs.Count > 0)
|
||||
{
|
||||
return;
|
||||
foreach (var msg in msgs)
|
||||
{
|
||||
NoticeManager.Instance.SendMessage(msg);
|
||||
}
|
||||
NoticeManager.Instance.Enqueue(Utils.List2String(msgs.Take(10).ToList(), true));
|
||||
if (!validatorResult.Success)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (blClipboard)
|
||||
|
|
@ -788,9 +829,18 @@ public class ProfilesViewModel : MyReactiveObject
|
|||
return;
|
||||
}
|
||||
var (context, validatorResult) = await CoreConfigContextBuilder.Build(_config, item);
|
||||
if (NoticeManager.Instance.NotifyValidatorResult(validatorResult) && !validatorResult.Success)
|
||||
var msgs = new List<string>([..validatorResult.Errors, ..validatorResult.Warnings]);
|
||||
if (msgs.Count > 0)
|
||||
{
|
||||
return;
|
||||
foreach (var msg in msgs)
|
||||
{
|
||||
NoticeManager.Instance.SendMessage(msg);
|
||||
}
|
||||
NoticeManager.Instance.Enqueue(Utils.List2String(msgs.Take(10).ToList(), true));
|
||||
if (!validatorResult.Success)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
var result = await CoreConfigHandler.GenerateClientConfig(context, fileName);
|
||||
if (result.Success != true)
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@
|
|||
<TabItem HorizontalAlignment="Left" Header="{x:Static resx:ResUI.menuServerList}">
|
||||
<Grid
|
||||
Margin="{StaticResource Margin8}"
|
||||
ColumnDefinitions="Auto,Auto,Auto"
|
||||
ColumnDefinitions="300,Auto,Auto"
|
||||
RowDefinitions="Auto,Auto,Auto">
|
||||
|
||||
<TextBlock
|
||||
|
|
@ -108,7 +108,7 @@
|
|||
x:Name="cmbSubChildItems"
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Width="600"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}"
|
||||
DisplayMemberBinding="{Binding Remarks}"
|
||||
ItemsSource="{Binding SubItems}" />
|
||||
|
|
@ -117,8 +117,7 @@
|
|||
Grid.Column="2"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbPolicyGroupSubChildTip}"
|
||||
TextWrapping="Wrap" />
|
||||
Text="{x:Static resx:ResUI.TbPolicyGroupSubChildTip}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
|
|
@ -126,14 +125,12 @@
|
|||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.LvFilter}" />
|
||||
<ComboBox
|
||||
x:Name="cmbFilter"
|
||||
<TextBox
|
||||
x:Name="txtFilter"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Width="600"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
IsEditable="True" />
|
||||
VerticalAlignment="Center" />
|
||||
</Grid>
|
||||
</TabItem>
|
||||
|
||||
|
|
@ -187,11 +184,11 @@
|
|||
Binding="{Binding ConfigType}"
|
||||
Header="{x:Static resx:ResUI.LvServiceType}" />
|
||||
<DataGridTextColumn
|
||||
Width="200"
|
||||
Width="150"
|
||||
Binding="{Binding Remarks}"
|
||||
Header="{x:Static resx:ResUI.LvRemarks}" />
|
||||
<DataGridTextColumn
|
||||
Width="200"
|
||||
Width="120"
|
||||
Binding="{Binding Address}"
|
||||
Header="{x:Static resx:ResUI.LvAddress}" />
|
||||
<DataGridTextColumn
|
||||
|
|
@ -229,11 +226,11 @@
|
|||
Binding="{Binding ConfigType}"
|
||||
Header="{x:Static resx:ResUI.LvServiceType}" />
|
||||
<DataGridTextColumn
|
||||
Width="200"
|
||||
Width="150"
|
||||
Binding="{Binding Remarks}"
|
||||
Header="{x:Static resx:ResUI.LvRemarks}" />
|
||||
<DataGridTextColumn
|
||||
Width="200"
|
||||
Width="120"
|
||||
Binding="{Binding Address}"
|
||||
Header="{x:Static resx:ResUI.LvAddress}" />
|
||||
<DataGridTextColumn
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ public partial class AddGroupServerWindow : WindowBase<AddGroupServerViewModel>
|
|||
ResUI.TbRoundRobin,
|
||||
ResUI.TbLeastLoad,
|
||||
};
|
||||
cmbFilter.ItemsSource = Global.PolicyGroupDefaultFilterList;
|
||||
|
||||
switch (profileItem.ConfigType)
|
||||
{
|
||||
|
|
@ -54,7 +53,7 @@ public partial class AddGroupServerWindow : WindowBase<AddGroupServerViewModel>
|
|||
this.Bind(ViewModel, vm => vm.PolicyGroupType, v => v.cmbPolicyGroupType.SelectedValue).DisposeWith(disposables);
|
||||
//this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.cmbSubChildItems.ItemsSource).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSubItem, v => v.cmbSubChildItems.SelectedItem).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.Filter, v => v.cmbFilter.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.Filter, v => v.txtFilter.Text).DisposeWith(disposables);
|
||||
|
||||
this.Bind(ViewModel, vm => vm.SelectedChild, v => v.lstChild.SelectedItem).DisposeWith(disposables);
|
||||
|
||||
|
|
@ -148,7 +147,14 @@ public partial class AddGroupServerWindow : WindowBase<AddGroupServerViewModel>
|
|||
private async void MenuAddChild_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
var selectWindow = new ProfilesSelectWindow();
|
||||
selectWindow.SetConfigTypeFilter([EConfigType.Custom], exclude: true);
|
||||
if (ViewModel?.SelectedSource?.ConfigType == EConfigType.PolicyGroup)
|
||||
{
|
||||
selectWindow.SetConfigTypeFilter(new[] { EConfigType.Custom }, exclude: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
selectWindow.SetConfigTypeFilter(new[] { EConfigType.Custom, EConfigType.PolicyGroup, EConfigType.ProxyChain }, exclude: true);
|
||||
}
|
||||
selectWindow.AllowMultiSelect(true);
|
||||
var result = await selectWindow.ShowDialog<bool?>(this);
|
||||
if (result == true)
|
||||
|
|
|
|||
|
|
@ -90,17 +90,17 @@
|
|||
Header="{x:Static resx:ResUI.LvServiceType}"
|
||||
Tag="ConfigType" />
|
||||
<DataGridTextColumn
|
||||
Width="200"
|
||||
Width="120"
|
||||
Binding="{Binding Remarks}"
|
||||
Header="{x:Static resx:ResUI.LvRemarks}"
|
||||
Tag="Remarks" />
|
||||
<DataGridTextColumn
|
||||
Width="200"
|
||||
Width="120"
|
||||
Binding="{Binding Address}"
|
||||
Header="{x:Static resx:ResUI.LvAddress}"
|
||||
Tag="Address" />
|
||||
<DataGridTextColumn
|
||||
Width="100"
|
||||
Width="60"
|
||||
Binding="{Binding Port}"
|
||||
Header="{x:Static resx:ResUI.LvPort}"
|
||||
Tag="Port" />
|
||||
|
|
|
|||
|
|
@ -193,9 +193,14 @@
|
|||
<MenuItem x:Name="menuExport2ShareUrlBase64" Header="{x:Static resx:ResUI.menuExport2ShareUrlBase64}" />
|
||||
</MenuItem>
|
||||
<Separator />
|
||||
<MenuItem Header="{x:Static resx:ResUI.menuGenGroupServer}">
|
||||
<MenuItem x:Name="menuGenGroupAllServer" Header="{x:Static resx:ResUI.menuAllServers}" />
|
||||
<MenuItem x:Name="menuGenGroupRegionServer" Header="{x:Static resx:ResUI.menuGenRegionGroup}" />
|
||||
<MenuItem Header="{x:Static resx:ResUI.menuGenGroupMultipleServer}">
|
||||
<MenuItem x:Name="menuGenGroupMultipleServerXrayRandom" Header="{x:Static resx:ResUI.menuGenGroupMultipleServerXrayRandom}" />
|
||||
<MenuItem x:Name="menuGenGroupMultipleServerXrayRoundRobin" Header="{x:Static resx:ResUI.menuGenGroupMultipleServerXrayRoundRobin}" />
|
||||
<MenuItem x:Name="menuGenGroupMultipleServerXrayLeastPing" Header="{x:Static resx:ResUI.menuGenGroupMultipleServerXrayLeastPing}" />
|
||||
<MenuItem x:Name="menuGenGroupMultipleServerXrayLeastLoad" Header="{x:Static resx:ResUI.menuGenGroupMultipleServerXrayLeastLoad}" />
|
||||
<Separator />
|
||||
<MenuItem x:Name="menuGenGroupMultipleServerSingBoxLeastPing" Header="{x:Static resx:ResUI.menuGenGroupMultipleServerSingBoxLeastPing}" />
|
||||
<MenuItem x:Name="menuGenGroupMultipleServerSingBoxFallback" Header="{x:Static resx:ResUI.menuGenGroupMultipleServerSingBoxFallback}" />
|
||||
</MenuItem>
|
||||
</ContextMenu>
|
||||
</DataGrid.ContextMenu>
|
||||
|
|
|
|||
|
|
@ -60,8 +60,12 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
|
|||
this.BindCommand(ViewModel, vm => vm.CopyServerCmd, v => v.menuCopyServer).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.SetDefaultServerCmd, v => v.menuSetDefaultServer).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.ShareServerCmd, v => v.menuShareServer).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.GenGroupAllServerCmd, v => v.menuGenGroupAllServer).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.GenGroupRegionServerCmd, v => v.menuGenGroupRegionServer).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerXrayRandomCmd, v => v.menuGenGroupMultipleServerXrayRandom).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerXrayRoundRobinCmd, v => v.menuGenGroupMultipleServerXrayRoundRobin).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerXrayLeastPingCmd, v => v.menuGenGroupMultipleServerXrayLeastPing).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerXrayLeastLoadCmd, v => v.menuGenGroupMultipleServerXrayLeastLoad).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerSingBoxLeastPingCmd, v => v.menuGenGroupMultipleServerSingBoxLeastPing).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerSingBoxFallbackCmd, v => v.menuGenGroupMultipleServerSingBoxFallback).DisposeWith(disposables);
|
||||
|
||||
//servers move
|
||||
//this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.cmbMoveToGroup.ItemsSource).DisposeWith(disposables);
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ public partial class SubEditWindow : WindowBase<SubEditViewModel>
|
|||
private async void BtnSelectPrevProfile_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
var selectWindow = new ProfilesSelectWindow();
|
||||
selectWindow.SetConfigTypeFilter([EConfigType.Custom], exclude: true);
|
||||
selectWindow.SetConfigTypeFilter(new[] { EConfigType.Custom, EConfigType.PolicyGroup, EConfigType.ProxyChain }, exclude: true);
|
||||
var result = await selectWindow.ShowDialog<bool?>(this);
|
||||
if (result == true)
|
||||
{
|
||||
|
|
@ -74,7 +74,7 @@ public partial class SubEditWindow : WindowBase<SubEditViewModel>
|
|||
private async void BtnSelectNextProfile_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
var selectWindow = new ProfilesSelectWindow();
|
||||
selectWindow.SetConfigTypeFilter([EConfigType.Custom], exclude: true);
|
||||
selectWindow.SetConfigTypeFilter(new[] { EConfigType.Custom, EConfigType.PolicyGroup, EConfigType.ProxyChain }, exclude: true);
|
||||
var result = await selectWindow.ShowDialog<bool?>(this);
|
||||
if (result == true)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@
|
|||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="300" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
|
@ -163,7 +163,7 @@
|
|||
x:Name="cmbSubChildItems"
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Width="600"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}"
|
||||
DisplayMemberPath="Remarks"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
|
|
@ -173,8 +173,7 @@
|
|||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbPolicyGroupSubChildTip}"
|
||||
TextWrapping="Wrap" />
|
||||
Text="{x:Static resx:ResUI.TbPolicyGroupSubChildTip}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
|
|
@ -183,15 +182,14 @@
|
|||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.LvFilter}" />
|
||||
<ComboBox
|
||||
x:Name="cmbFilter"
|
||||
<TextBox
|
||||
x:Name="txtFilter"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Width="600"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
IsEditable="True"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
AcceptsReturn="True"
|
||||
Style="{StaticResource DefTextBox}" />
|
||||
</Grid>
|
||||
</TabItem>
|
||||
|
||||
|
|
@ -253,11 +251,11 @@
|
|||
Binding="{Binding ConfigType}"
|
||||
Header="{x:Static resx:ResUI.LvServiceType}" />
|
||||
<DataGridTextColumn
|
||||
Width="200"
|
||||
Width="150"
|
||||
Binding="{Binding Remarks}"
|
||||
Header="{x:Static resx:ResUI.LvRemarks}" />
|
||||
<DataGridTextColumn
|
||||
Width="200"
|
||||
Width="120"
|
||||
Binding="{Binding Address}"
|
||||
Header="{x:Static resx:ResUI.LvAddress}" />
|
||||
<DataGridTextColumn
|
||||
|
|
@ -294,11 +292,11 @@
|
|||
Binding="{Binding ConfigType}"
|
||||
Header="{x:Static resx:ResUI.LvServiceType}" />
|
||||
<DataGridTextColumn
|
||||
Width="200"
|
||||
Width="150"
|
||||
Binding="{Binding Remarks}"
|
||||
Header="{x:Static resx:ResUI.LvRemarks}" />
|
||||
<DataGridTextColumn
|
||||
Width="200"
|
||||
Width="120"
|
||||
Binding="{Binding Address}"
|
||||
Header="{x:Static resx:ResUI.LvAddress}" />
|
||||
<DataGridTextColumn
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ public partial class AddGroupServerWindow
|
|||
ResUI.TbRoundRobin,
|
||||
ResUI.TbLeastLoad,
|
||||
};
|
||||
cmbFilter.ItemsSource = Global.PolicyGroupDefaultFilterList;
|
||||
|
||||
switch (profileItem.ConfigType)
|
||||
{
|
||||
|
|
@ -49,7 +48,7 @@ public partial class AddGroupServerWindow
|
|||
this.Bind(ViewModel, vm => vm.PolicyGroupType, v => v.cmbPolicyGroupType.Text).DisposeWith(disposables);
|
||||
this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.cmbSubChildItems.ItemsSource).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSubItem, v => v.cmbSubChildItems.SelectedItem).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.Filter, v => v.cmbFilter.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.Filter, v => v.txtFilter.Text).DisposeWith(disposables);
|
||||
|
||||
this.OneWayBind(ViewModel, vm => vm.ChildItemsObs, v => v.lstChild.ItemsSource).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedChild, v => v.lstChild.SelectedItem).DisposeWith(disposables);
|
||||
|
|
@ -128,7 +127,14 @@ public partial class AddGroupServerWindow
|
|||
private async void MenuAddChild_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var selectWindow = new ProfilesSelectWindow();
|
||||
selectWindow.SetConfigTypeFilter([EConfigType.Custom], exclude: true);
|
||||
if (ViewModel?.SelectedSource?.ConfigType == EConfigType.PolicyGroup)
|
||||
{
|
||||
selectWindow.SetConfigTypeFilter(new[] { EConfigType.Custom }, exclude: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
selectWindow.SetConfigTypeFilter(new[] { EConfigType.Custom, EConfigType.PolicyGroup, EConfigType.ProxyChain }, exclude: true);
|
||||
}
|
||||
selectWindow.AllowMultiSelect(true);
|
||||
if (selectWindow.ShowDialog() == true)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -119,17 +119,17 @@
|
|||
ExName="ConfigType"
|
||||
Header="{x:Static resx:ResUI.LvServiceType}" />
|
||||
<base:MyDGTextColumn
|
||||
Width="200"
|
||||
Width="150"
|
||||
Binding="{Binding Remarks}"
|
||||
ExName="Remarks"
|
||||
Header="{x:Static resx:ResUI.LvRemarks}" />
|
||||
<base:MyDGTextColumn
|
||||
Width="200"
|
||||
Width="120"
|
||||
Binding="{Binding Address}"
|
||||
ExName="Address"
|
||||
Header="{x:Static resx:ResUI.LvAddress}" />
|
||||
<base:MyDGTextColumn
|
||||
Width="100"
|
||||
Width="60"
|
||||
Binding="{Binding Port}"
|
||||
ExName="Port"
|
||||
Header="{x:Static resx:ResUI.LvPort}" />
|
||||
|
|
|
|||
|
|
@ -240,15 +240,32 @@
|
|||
Header="{x:Static resx:ResUI.menuExport2ShareUrlBase64}" />
|
||||
</MenuItem>
|
||||
<Separator />
|
||||
<MenuItem Header="{x:Static resx:ResUI.menuGenGroupServer}">
|
||||
<MenuItem Header="{x:Static resx:ResUI.menuGenGroupMultipleServer}">
|
||||
<MenuItem
|
||||
x:Name="menuGenGroupAllServer"
|
||||
x:Name="menuGenGroupMultipleServerXrayRandom"
|
||||
Height="{StaticResource MenuItemHeight}"
|
||||
Header="{x:Static resx:ResUI.menuAllServers}" />
|
||||
Header="{x:Static resx:ResUI.menuGenGroupMultipleServerXrayRandom}" />
|
||||
<MenuItem
|
||||
x:Name="menuGenGroupRegionServer"
|
||||
x:Name="menuGenGroupMultipleServerXrayRoundRobin"
|
||||
Height="{StaticResource MenuItemHeight}"
|
||||
Header="{x:Static resx:ResUI.menuGenRegionGroup}" />
|
||||
Header="{x:Static resx:ResUI.menuGenGroupMultipleServerXrayRoundRobin}" />
|
||||
<MenuItem
|
||||
x:Name="menuGenGroupMultipleServerXrayLeastPing"
|
||||
Height="{StaticResource MenuItemHeight}"
|
||||
Header="{x:Static resx:ResUI.menuGenGroupMultipleServerXrayLeastPing}" />
|
||||
<MenuItem
|
||||
x:Name="menuGenGroupMultipleServerXrayLeastLoad"
|
||||
Height="{StaticResource MenuItemHeight}"
|
||||
Header="{x:Static resx:ResUI.menuGenGroupMultipleServerXrayLeastLoad}" />
|
||||
<Separator />
|
||||
<MenuItem
|
||||
x:Name="menuGenGroupMultipleServerSingBoxLeastPing"
|
||||
Height="{StaticResource MenuItemHeight}"
|
||||
Header="{x:Static resx:ResUI.menuGenGroupMultipleServerSingBoxLeastPing}" />
|
||||
<MenuItem
|
||||
x:Name="menuGenGroupMultipleServerSingBoxFallback"
|
||||
Height="{StaticResource MenuItemHeight}"
|
||||
Header="{x:Static resx:ResUI.menuGenGroupMultipleServerSingBoxFallback}" />
|
||||
</MenuItem>
|
||||
</ContextMenu>
|
||||
</DataGrid.ContextMenu>
|
||||
|
|
|
|||
|
|
@ -54,8 +54,12 @@ public partial class ProfilesView
|
|||
this.BindCommand(ViewModel, vm => vm.CopyServerCmd, v => v.menuCopyServer).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.SetDefaultServerCmd, v => v.menuSetDefaultServer).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.ShareServerCmd, v => v.menuShareServer).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.GenGroupAllServerCmd, v => v.menuGenGroupAllServer).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.GenGroupRegionServerCmd, v => v.menuGenGroupRegionServer).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerXrayRandomCmd, v => v.menuGenGroupMultipleServerXrayRandom).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerXrayRoundRobinCmd, v => v.menuGenGroupMultipleServerXrayRoundRobin).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerXrayLeastPingCmd, v => v.menuGenGroupMultipleServerXrayLeastPing).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerXrayLeastLoadCmd, v => v.menuGenGroupMultipleServerXrayLeastLoad).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerSingBoxLeastPingCmd, v => v.menuGenGroupMultipleServerSingBoxLeastPing).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerSingBoxFallbackCmd, v => v.menuGenGroupMultipleServerSingBoxFallback).DisposeWith(disposables);
|
||||
|
||||
//servers move
|
||||
this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.cmbMoveToGroup.ItemsSource).DisposeWith(disposables);
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ public partial class SubEditWindow
|
|||
private async void BtnSelectPrevProfile_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var selectWindow = new ProfilesSelectWindow();
|
||||
selectWindow.SetConfigTypeFilter([EConfigType.Custom], exclude: true);
|
||||
selectWindow.SetConfigTypeFilter(new[] { EConfigType.Custom, EConfigType.PolicyGroup, EConfigType.ProxyChain }, exclude: true);
|
||||
if (selectWindow.ShowDialog() == true)
|
||||
{
|
||||
var profile = await selectWindow.ProfileItem;
|
||||
|
|
@ -67,7 +67,7 @@ public partial class SubEditWindow
|
|||
private async void BtnSelectNextProfile_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var selectWindow = new ProfilesSelectWindow();
|
||||
selectWindow.SetConfigTypeFilter([EConfigType.Custom], exclude: true);
|
||||
selectWindow.SetConfigTypeFilter(new[] { EConfigType.Custom, EConfigType.PolicyGroup, EConfigType.ProxyChain }, exclude: true);
|
||||
if (selectWindow.ShowDialog() == true)
|
||||
{
|
||||
var profile = await selectWindow.ProfileItem;
|
||||
|
|
|
|||
Loading…
Reference in a new issue