mirror of
https://github.com/2dust/v2rayN.git
synced 2025-10-27 10:40:08 +00:00
Compare commits
4 commits
59770a8515
...
97c1cc09f3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
97c1cc09f3 | ||
|
|
6ffb3bd30c | ||
|
|
2826444ffc | ||
|
|
d6c81c3a9c |
19 changed files with 432 additions and 27 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
<Project>
|
<Project>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Version>7.14.7</Version>
|
<Version>7.14.8</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
|
|
||||||
63
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
63
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
|
|
@ -114,6 +114,33 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Core '{0}' does not support network type '{1}'. 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string CoreNotSupportNetwork {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("CoreNotSupportNetwork", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Core '{0}' does not support protocol '{1}'. 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string CoreNotSupportProtocol {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("CoreNotSupportProtocol", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Core '{0}' does not support protocol '{1}' when using transport '{2}'. 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string CoreNotSupportProtocolTransport {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("CoreNotSupportProtocolTransport", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Note that custom configuration relies entirely on your own configuration and does not work with all settings. If you want to use the system proxy, please modify the listening port manually. 的本地化字符串。
|
/// 查找类似 Note that custom configuration relies entirely on your own configuration and does not work with all settings. If you want to use the system proxy, please modify the listening port manually. 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -2040,6 +2067,24 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Proxy chained node alias '{0}' does not exist. 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string ProxyChainedNodeTagNotExist {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("ProxyChainedNodeTagNotExist", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Proxy chained node remark '{0}' refers to an outbound that does not support config type '{1}'. 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string ProxyChainedNodeTagNotSupportConfigType {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("ProxyChainedNodeTagNotSupportConfigType", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Global hotkey {0} registration failed, reason: {1} 的本地化字符串。
|
/// 查找类似 Global hotkey {0} registration failed, reason: {1} 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -2103,6 +2148,24 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Routing rule references outbound remark '{0}', but no outbound with this remark exists. 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string RoutingRuleOutboundTagNotExist {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("RoutingRuleOutboundTagNotExist", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Routing rule outbound remark '{0}' refers to an outbound that does not support config type '{1}'. 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string RoutingRuleOutboundTagNotSupportConfigType {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("RoutingRuleOutboundTagNotSupportConfigType", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Run as Admin 的本地化字符串。
|
/// 查找类似 Run as Admin 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -1515,4 +1515,25 @@
|
||||||
<data name="TbSelectProfile" xml:space="preserve">
|
<data name="TbSelectProfile" xml:space="preserve">
|
||||||
<value>Select Profile</value>
|
<value>Select Profile</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="CoreNotSupportNetwork" xml:space="preserve">
|
||||||
|
<value>Core '{0}' does not support network type '{1}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="CoreNotSupportProtocolTransport" xml:space="preserve">
|
||||||
|
<value>Core '{0}' does not support protocol '{1}' when using transport '{2}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="CoreNotSupportProtocol" xml:space="preserve">
|
||||||
|
<value>Core '{0}' does not support protocol '{1}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="RoutingRuleOutboundTagNotExist" xml:space="preserve">
|
||||||
|
<value>Routing rule references outbound remark '{0}', but no outbound with this remark exists.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ProxyChainedNodeTagNotSupportConfigType" xml:space="preserve">
|
||||||
|
<value>Proxy chained node remark '{0}' refers to an outbound that does not support config type '{1}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ProxyChainedNodeTagNotExist" xml:space="preserve">
|
||||||
|
<value>Proxy chained node alias '{0}' does not exist.</value>
|
||||||
|
</data>
|
||||||
|
<data name="RoutingRuleOutboundTagNotSupportConfigType" xml:space="preserve">
|
||||||
|
<value>Routing rule outbound remark '{0}' refers to an outbound that does not support config type '{1}'.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -1515,4 +1515,25 @@
|
||||||
<data name="TbSelectProfile" xml:space="preserve">
|
<data name="TbSelectProfile" xml:space="preserve">
|
||||||
<value>Select Profile</value>
|
<value>Select Profile</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="CoreNotSupportNetwork" xml:space="preserve">
|
||||||
|
<value>Core '{0}' does not support network type '{1}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="CoreNotSupportProtocolTransport" xml:space="preserve">
|
||||||
|
<value>Core '{0}' does not support protocol '{1}' when using transport '{2}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="CoreNotSupportProtocol" xml:space="preserve">
|
||||||
|
<value>Core '{0}' does not support protocol '{1}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="RoutingRuleOutboundTagNotExist" xml:space="preserve">
|
||||||
|
<value>Routing rule references outbound remark '{0}', but no outbound with this remark exists.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ProxyChainedNodeTagNotSupportConfigType" xml:space="preserve">
|
||||||
|
<value>Proxy chained node remark '{0}' refers to an outbound that does not support config type '{1}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ProxyChainedNodeTagNotExist" xml:space="preserve">
|
||||||
|
<value>Proxy chained node alias '{0}' does not exist.</value>
|
||||||
|
</data>
|
||||||
|
<data name="RoutingRuleOutboundTagNotSupportConfigType" xml:space="preserve">
|
||||||
|
<value>Routing rule outbound remark '{0}' refers to an outbound that does not support config type '{1}'.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -1515,4 +1515,25 @@
|
||||||
<data name="TbSelectProfile" xml:space="preserve">
|
<data name="TbSelectProfile" xml:space="preserve">
|
||||||
<value>Select Profile</value>
|
<value>Select Profile</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="CoreNotSupportNetwork" xml:space="preserve">
|
||||||
|
<value>Core '{0}' does not support network type '{1}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="CoreNotSupportProtocolTransport" xml:space="preserve">
|
||||||
|
<value>Core '{0}' does not support protocol '{1}' when using transport '{2}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="CoreNotSupportProtocol" xml:space="preserve">
|
||||||
|
<value>Core '{0}' does not support protocol '{1}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="RoutingRuleOutboundTagNotExist" xml:space="preserve">
|
||||||
|
<value>Routing rule references outbound remark '{0}', but no outbound with this remark exists.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ProxyChainedNodeTagNotSupportConfigType" xml:space="preserve">
|
||||||
|
<value>Proxy chained node remark '{0}' refers to an outbound that does not support config type '{1}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ProxyChainedNodeTagNotExist" xml:space="preserve">
|
||||||
|
<value>Proxy chained node alias '{0}' does not exist.</value>
|
||||||
|
</data>
|
||||||
|
<data name="RoutingRuleOutboundTagNotSupportConfigType" xml:space="preserve">
|
||||||
|
<value>Routing rule outbound remark '{0}' refers to an outbound that does not support config type '{1}'.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -1515,4 +1515,25 @@
|
||||||
<data name="TbSelectProfile" xml:space="preserve">
|
<data name="TbSelectProfile" xml:space="preserve">
|
||||||
<value>Select Profile</value>
|
<value>Select Profile</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="CoreNotSupportNetwork" xml:space="preserve">
|
||||||
|
<value>Core '{0}' does not support network type '{1}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="CoreNotSupportProtocolTransport" xml:space="preserve">
|
||||||
|
<value>Core '{0}' does not support protocol '{1}' when using transport '{2}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="CoreNotSupportProtocol" xml:space="preserve">
|
||||||
|
<value>Core '{0}' does not support protocol '{1}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="RoutingRuleOutboundTagNotExist" xml:space="preserve">
|
||||||
|
<value>Routing rule references outbound remark '{0}', but no outbound with this remark exists.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ProxyChainedNodeTagNotSupportConfigType" xml:space="preserve">
|
||||||
|
<value>Proxy chained node remark '{0}' refers to an outbound that does not support config type '{1}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ProxyChainedNodeTagNotExist" xml:space="preserve">
|
||||||
|
<value>Proxy chained node alias '{0}' does not exist.</value>
|
||||||
|
</data>
|
||||||
|
<data name="RoutingRuleOutboundTagNotSupportConfigType" xml:space="preserve">
|
||||||
|
<value>Routing rule outbound remark '{0}' refers to an outbound that does not support config type '{1}'.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -1512,4 +1512,25 @@
|
||||||
<data name="TbSelectProfile" xml:space="preserve">
|
<data name="TbSelectProfile" xml:space="preserve">
|
||||||
<value>选择配置文件</value>
|
<value>选择配置文件</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="CoreNotSupportNetwork" xml:space="preserve">
|
||||||
|
<value>核心 '{0}' 不支持网络类型 '{1}'。</value>
|
||||||
|
</data>
|
||||||
|
<data name="CoreNotSupportProtocolTransport" xml:space="preserve">
|
||||||
|
<value>核心 '{0}' 在使用传输方式 '{2}' 时不支持协议 '{1}'。</value>
|
||||||
|
</data>
|
||||||
|
<data name="CoreNotSupportProtocol" xml:space="preserve">
|
||||||
|
<value>核心 '{0}' 不支持协议 '{1}'。</value>
|
||||||
|
</data>
|
||||||
|
<data name="RoutingRuleOutboundTagNotExist" xml:space="preserve">
|
||||||
|
<value>路由规则引用了出站别名 '{0}',但不存在具有该别名的出站。</value>
|
||||||
|
</data>
|
||||||
|
<data name="ProxyChainedNodeTagNotSupportConfigType" xml:space="preserve">
|
||||||
|
<value>代理链节点别名 '{0}' 引用的出站不支持配置类型 '{1}'。</value>
|
||||||
|
</data>
|
||||||
|
<data name="ProxyChainedNodeTagNotExist" xml:space="preserve">
|
||||||
|
<value>代理链节点别名 '{0}' 不存在。</value>
|
||||||
|
</data>
|
||||||
|
<data name="RoutingRuleOutboundTagNotSupportConfigType" xml:space="preserve">
|
||||||
|
<value>路由规则出站别名 '{0}' 引用的出站不支持配置类型 '{1}'。</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -1512,4 +1512,25 @@
|
||||||
<data name="TbSelectProfile" xml:space="preserve">
|
<data name="TbSelectProfile" xml:space="preserve">
|
||||||
<value>Select Profile</value>
|
<value>Select Profile</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="CoreNotSupportNetwork" xml:space="preserve">
|
||||||
|
<value>Core '{0}' does not support network type '{1}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="CoreNotSupportProtocolTransport" xml:space="preserve">
|
||||||
|
<value>Core '{0}' does not support protocol '{1}' when using transport '{2}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="CoreNotSupportProtocol" xml:space="preserve">
|
||||||
|
<value>Core '{0}' does not support protocol '{1}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="RoutingRuleOutboundTagNotExist" xml:space="preserve">
|
||||||
|
<value>Routing rule references outbound remark '{0}', but no outbound with this remark exists.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ProxyChainedNodeTagNotSupportConfigType" xml:space="preserve">
|
||||||
|
<value>Proxy chained node remark '{0}' refers to an outbound that does not support config type '{1}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ProxyChainedNodeTagNotExist" xml:space="preserve">
|
||||||
|
<value>Proxy chained node alias '{0}' does not exist.</value>
|
||||||
|
</data>
|
||||||
|
<data name="RoutingRuleOutboundTagNotSupportConfigType" xml:space="preserve">
|
||||||
|
<value>Routing rule outbound remark '{0}' refers to an outbound that does not support config type '{1}'.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
211
v2rayN/ServiceLib/Services/ActionPrecheckService.cs
Normal file
211
v2rayN/ServiceLib/Services/ActionPrecheckService.cs
Normal file
|
|
@ -0,0 +1,211 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using DynamicData;
|
||||||
|
using ServiceLib.Models;
|
||||||
|
|
||||||
|
namespace ServiceLib.Services;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Centralized pre-checks before sensitive actions (set active profile, generate config, etc.).
|
||||||
|
/// Return (ok, msg) for VMs to decide.
|
||||||
|
/// </summary>
|
||||||
|
public class ActionPrecheckService
|
||||||
|
{
|
||||||
|
private static readonly Lazy<ActionPrecheckService> _instance = new(() => new ActionPrecheckService(AppManager.Instance.Config));
|
||||||
|
public static ActionPrecheckService Instance => _instance.Value;
|
||||||
|
|
||||||
|
private readonly Config _config;
|
||||||
|
|
||||||
|
public ActionPrecheckService(Config config)
|
||||||
|
{
|
||||||
|
_config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<string> OneMsg(string msg) => new() { msg };
|
||||||
|
|
||||||
|
public async Task<(bool ok, List<string> msgs)> CheckBeforeSetActive(string? indexId)
|
||||||
|
{
|
||||||
|
if (indexId.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
return (false, OneMsg(ResUI.PleaseSelectServer));
|
||||||
|
}
|
||||||
|
|
||||||
|
var item = await AppManager.Instance.GetProfileItem(indexId);
|
||||||
|
if (item is null)
|
||||||
|
{
|
||||||
|
return (false, OneMsg(ResUI.PleaseSelectServer));
|
||||||
|
}
|
||||||
|
|
||||||
|
return await CheckBeforeGenerateConfig(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<(bool ok, List<string> msgs)> CheckBeforeGenerateConfig(ProfileItem? item)
|
||||||
|
{
|
||||||
|
if (item is null)
|
||||||
|
{
|
||||||
|
return (false, OneMsg(ResUI.PleaseSelectServer));
|
||||||
|
}
|
||||||
|
|
||||||
|
var msgs = new List<string>();
|
||||||
|
|
||||||
|
var currentNodeMsgs = ValidateCurrentNodeAndCoreSupport(item).ToList();
|
||||||
|
if (currentNodeMsgs.Count > 0)
|
||||||
|
{
|
||||||
|
msgs.AddRange(currentNodeMsgs);
|
||||||
|
return (false, msgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
msgs.AddRange(await ValidateRelatedNodesExistAndValid(item));
|
||||||
|
|
||||||
|
return (true, msgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<string> ValidateCurrentNodeAndCoreSupport(ProfileItem item)
|
||||||
|
{
|
||||||
|
var coreType = AppManager.Instance.GetCoreType(item, item.ConfigType);
|
||||||
|
return ValidateNodeAndCoreSupport(item, coreType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <summary>
|
||||||
|
/// Validate whether the node and chosen core combination is supported. Returns a collection of messages to show the user.
|
||||||
|
/// An empty collection means there are no blocking errors.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
private IEnumerable<string> ValidateNodeAndCoreSupport(ProfileItem item, ECoreType? coreType = null)
|
||||||
|
{
|
||||||
|
// sing-box does not support xhttp / kcp
|
||||||
|
// sing-box does not support transports like ws/http/httpupgrade/etc. when the node is not vmess/trojan/vless
|
||||||
|
coreType ??= AppManager.Instance.GetCoreType(item, item.ConfigType);
|
||||||
|
var net = item.GetNetwork() ?? item.Network;
|
||||||
|
|
||||||
|
if (coreType == ECoreType.sing_box)
|
||||||
|
{
|
||||||
|
if (net is nameof(ETransport.kcp) or nameof(ETransport.xhttp))
|
||||||
|
{
|
||||||
|
yield return string.Format(ResUI.CoreNotSupportNetwork, nameof(ECoreType.sing_box), net);
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.ConfigType is not (EConfigType.VMess or EConfigType.VLESS or EConfigType.Trojan))
|
||||||
|
{
|
||||||
|
if (net is nameof(ETransport.ws) or nameof(ETransport.http) or nameof(ETransport.h2) or nameof(ETransport.quic) or nameof(ETransport.httpupgrade))
|
||||||
|
{
|
||||||
|
yield return string.Format(ResUI.CoreNotSupportProtocolTransport, nameof(ECoreType.sing_box), item.ConfigType.ToString(), net);
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (coreType is ECoreType.Xray)
|
||||||
|
{
|
||||||
|
// Xray core does not support these protocols
|
||||||
|
if (item.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls)
|
||||||
|
{
|
||||||
|
yield return string.Format(ResUI.CoreNotSupportProtocol, nameof(ECoreType.Xray), item.ConfigType.ToString());
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
yield break; // explicit for clarity; no blocking errors
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Validate that nodes related to the current node (chained/routing) exist and are valid.
|
||||||
|
/// </summary>
|
||||||
|
private async Task<IEnumerable<string>> ValidateRelatedNodesExistAndValid(ProfileItem? item)
|
||||||
|
{
|
||||||
|
var msgs = new List<string>();
|
||||||
|
msgs.AddRange(await ValidateProxyChainedNodeExistAndValid(item));
|
||||||
|
msgs.AddRange(await ValidateRoutingNodeExistAndValid(item));
|
||||||
|
return msgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<IEnumerable<string>> ValidateProxyChainedNodeExistAndValid(ProfileItem? item)
|
||||||
|
{
|
||||||
|
var msgs = new List<string>();
|
||||||
|
if (item is null)
|
||||||
|
{
|
||||||
|
return msgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
// prev node and next node
|
||||||
|
var subItem = await AppManager.Instance.GetSubItem(item.Subid);
|
||||||
|
if (subItem is null)
|
||||||
|
{
|
||||||
|
return msgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
var prevNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
||||||
|
var nextNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.NextProfile);
|
||||||
|
var coreType = AppManager.Instance.GetCoreType(item, item.ConfigType);
|
||||||
|
|
||||||
|
CollectProxyChainedNodeValidation(prevNode, subItem.PrevProfile, coreType, msgs);
|
||||||
|
CollectProxyChainedNodeValidation(nextNode, subItem.NextProfile, coreType, msgs);
|
||||||
|
|
||||||
|
return msgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CollectProxyChainedNodeValidation(ProfileItem? node, string tag, ECoreType coreType, List<string> msgs)
|
||||||
|
{
|
||||||
|
if (node is not null)
|
||||||
|
{
|
||||||
|
msgs.AddRange(ValidateNodeAndCoreSupport(node, coreType));
|
||||||
|
if (node.ConfigType is EConfigType.Custom)
|
||||||
|
{
|
||||||
|
msgs.Add(string.Format(ResUI.ProxyChainedNodeTagNotSupportConfigType, node.Remarks, node.ConfigType.ToString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (tag.IsNotEmpty())
|
||||||
|
{
|
||||||
|
msgs.Add(string.Format(ResUI.ProxyChainedNodeTagNotExist, tag));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<IEnumerable<string>> ValidateRoutingNodeExistAndValid(ProfileItem? item)
|
||||||
|
{
|
||||||
|
var msgs = new List<string>();
|
||||||
|
|
||||||
|
if (item is null)
|
||||||
|
{
|
||||||
|
return msgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
var coreType = AppManager.Instance.GetCoreType(item, item.ConfigType);
|
||||||
|
var routing = await ConfigHandler.GetDefaultRouting(_config);
|
||||||
|
if (routing == null)
|
||||||
|
{
|
||||||
|
return msgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet);
|
||||||
|
foreach (var ruleItem in rules ?? [])
|
||||||
|
{
|
||||||
|
if (!ruleItem.Enabled)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var outboundTag = ruleItem.OutboundTag;
|
||||||
|
if (outboundTag.IsNullOrEmpty() || Global.OutboundTags.Contains(outboundTag))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tagItem = await AppManager.Instance.GetProfileItemViaRemarks(outboundTag);
|
||||||
|
if (tagItem is null)
|
||||||
|
{
|
||||||
|
msgs.Add(string.Format(ResUI.RoutingRuleOutboundTagNotExist, outboundTag));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
msgs.AddRange(ValidateNodeAndCoreSupport(tagItem, coreType));
|
||||||
|
if (tagItem.ConfigType is EConfigType.Custom)
|
||||||
|
{
|
||||||
|
msgs.Add(string.Format(ResUI.RoutingRuleOutboundTagNotSupportConfigType, outboundTag, tagItem.ConfigType.ToString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return msgs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
using System.Reactive;
|
using System.Reactive;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
using ReactiveUI.Fody.Helpers;
|
using ReactiveUI.Fody.Helpers;
|
||||||
using Splat;
|
|
||||||
|
|
||||||
namespace ServiceLib.ViewModels;
|
namespace ServiceLib.ViewModels;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,22 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reactive;
|
|
||||||
using System.Reactive.Linq;
|
using System.Reactive.Linq;
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using DynamicData;
|
using DynamicData;
|
||||||
using DynamicData.Binding;
|
using DynamicData.Binding;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
using ReactiveUI.Fody.Helpers;
|
using ReactiveUI.Fody.Helpers;
|
||||||
using Splat;
|
|
||||||
|
|
||||||
namespace ServiceLib.ViewModels;
|
namespace ServiceLib.ViewModels;
|
||||||
|
|
||||||
public class ProfilesSelectViewModel : MyReactiveObject
|
public class ProfilesSelectViewModel : MyReactiveObject
|
||||||
{
|
{
|
||||||
#region private prop
|
#region private prop
|
||||||
|
|
||||||
private List<ProfileItem> _lstProfile;
|
|
||||||
private string _serverFilter = string.Empty;
|
private string _serverFilter = string.Empty;
|
||||||
private Dictionary<string, bool> _dicHeaderSort = new();
|
private Dictionary<string, bool> _dicHeaderSort = new();
|
||||||
private string _subIndexId = string.Empty;
|
private string _subIndexId = string.Empty;
|
||||||
|
|
||||||
// ConfigType filter state: default include-mode with all types selected
|
// ConfigType filter state: default include-mode with all types selected
|
||||||
private List<EConfigType> _filterConfigTypes = new();
|
private List<EConfigType> _filterConfigTypes = new();
|
||||||
|
|
||||||
private bool _filterExclude = false;
|
private bool _filterExclude = false;
|
||||||
|
|
||||||
#endregion private prop
|
#endregion private prop
|
||||||
|
|
@ -66,6 +61,7 @@ public class ProfilesSelectViewModel : MyReactiveObject
|
||||||
_config = AppManager.Instance.Config;
|
_config = AppManager.Instance.Config;
|
||||||
_updateView = updateView;
|
_updateView = updateView;
|
||||||
_subIndexId = _config.SubIndexId ?? string.Empty;
|
_subIndexId = _config.SubIndexId ?? string.Empty;
|
||||||
|
|
||||||
#region WhenAnyValue && ReactiveCommand
|
#region WhenAnyValue && ReactiveCommand
|
||||||
|
|
||||||
this.WhenAnyValue(
|
this.WhenAnyValue(
|
||||||
|
|
@ -130,6 +126,7 @@ public class ProfilesSelectViewModel : MyReactiveObject
|
||||||
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Actions
|
#endregion Actions
|
||||||
|
|
||||||
#region Servers && Groups
|
#region Servers && Groups
|
||||||
|
|
@ -168,7 +165,6 @@ public class ProfilesSelectViewModel : MyReactiveObject
|
||||||
private async Task RefreshServersBiz()
|
private async Task RefreshServersBiz()
|
||||||
{
|
{
|
||||||
var lstModel = await GetProfileItemsEx(_subIndexId, _serverFilter);
|
var lstModel = await GetProfileItemsEx(_subIndexId, _serverFilter);
|
||||||
_lstProfile = JsonUtils.Deserialize<List<ProfileItem>>(JsonUtils.Serialize(lstModel)) ?? [];
|
|
||||||
|
|
||||||
ProfileItems.Clear();
|
ProfileItems.Clear();
|
||||||
ProfileItems.AddRange(lstModel);
|
ProfileItems.AddRange(lstModel);
|
||||||
|
|
@ -211,9 +207,6 @@ public class ProfilesSelectViewModel : MyReactiveObject
|
||||||
private async Task<List<ProfileItemModel>?> GetProfileItemsEx(string subid, string filter)
|
private async Task<List<ProfileItemModel>?> GetProfileItemsEx(string subid, string filter)
|
||||||
{
|
{
|
||||||
var lstModel = await AppManager.Instance.ProfileItems(_subIndexId, filter);
|
var lstModel = await AppManager.Instance.ProfileItems(_subIndexId, filter);
|
||||||
|
|
||||||
//await ConfigHandler.SetDefaultServer(_config, lstModel);
|
|
||||||
|
|
||||||
lstModel = (from t in lstModel
|
lstModel = (from t in lstModel
|
||||||
select new ProfileItemModel
|
select new ProfileItemModel
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -594,6 +594,16 @@ public class ProfilesViewModel : MyReactiveObject
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (ok, msgs) = await ActionPrecheckService.Instance.CheckBeforeSetActive(indexId);
|
||||||
|
if (msgs.Count > 0)
|
||||||
|
{
|
||||||
|
NoticeManager.Instance.Enqueue(msgs.First());
|
||||||
|
}
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (await ConfigHandler.SetDefaultServerIndex(_config, indexId) == 0)
|
if (await ConfigHandler.SetDefaultServerIndex(_config, indexId) == 0)
|
||||||
{
|
{
|
||||||
await RefreshServers();
|
await RefreshServers();
|
||||||
|
|
@ -757,6 +767,16 @@ public class ProfilesViewModel : MyReactiveObject
|
||||||
NoticeManager.Instance.Enqueue(ResUI.PleaseSelectServer);
|
NoticeManager.Instance.Enqueue(ResUI.PleaseSelectServer);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (ok, msgs) = await ActionPrecheckService.Instance.CheckBeforeGenerateConfig(item);
|
||||||
|
if (msgs.Count > 0)
|
||||||
|
{
|
||||||
|
NoticeManager.Instance.Enqueue(msgs.First());
|
||||||
|
}
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (blClipboard)
|
if (blClipboard)
|
||||||
{
|
{
|
||||||
var result = await CoreConfigHandler.GenerateClientConfig(item, null);
|
var result = await CoreConfigHandler.GenerateClientConfig(item, null);
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public RoutingItem SelectedRouting { get; set; }
|
public RoutingItem SelectedRouting { get; set; }
|
||||||
|
|
||||||
public IObservableCollection<RulesItemModel> RulesItems { get; } = new ObservableCollectionExtended<RulesItemModel>();
|
public IObservableCollection<RulesItemModel> RulesItems { get; } = new ObservableCollectionExtended<RulesItemModel>();
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,12 @@
|
||||||
using System.Linq;
|
|
||||||
using System.Reactive.Disposables;
|
using System.Reactive.Disposables;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.Primitives;
|
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.Markup.Xaml;
|
|
||||||
using Avalonia.VisualTree;
|
|
||||||
using Avalonia.ReactiveUI;
|
using Avalonia.ReactiveUI;
|
||||||
|
using Avalonia.VisualTree;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
using ServiceLib.Manager;
|
using ServiceLib.Manager;
|
||||||
using v2rayN.Desktop.Common;
|
|
||||||
|
|
||||||
namespace v2rayN.Desktop.Views;
|
namespace v2rayN.Desktop.Views;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ using Avalonia.Controls;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
using v2rayN.Desktop.Base;
|
using v2rayN.Desktop.Base;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace v2rayN.Desktop.Views;
|
namespace v2rayN.Desktop.Views;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ using Avalonia;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
using v2rayN.Desktop.Base;
|
using v2rayN.Desktop.Base;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace v2rayN.Desktop.Views;
|
namespace v2rayN.Desktop.Views;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,8 @@ using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using System.Windows.Controls.Primitives;
|
using System.Windows.Controls.Primitives;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using System.Windows.Threading;
|
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
using ServiceLib.Manager;
|
using ServiceLib.Manager;
|
||||||
using Splat;
|
|
||||||
using v2rayN.Base;
|
using v2rayN.Base;
|
||||||
|
|
||||||
namespace v2rayN.Views;
|
namespace v2rayN.Views;
|
||||||
|
|
@ -34,7 +32,6 @@ public partial class ProfilesSelectWindow
|
||||||
|
|
||||||
ViewModel = new ProfilesSelectViewModel(UpdateViewHandler);
|
ViewModel = new ProfilesSelectViewModel(UpdateViewHandler);
|
||||||
|
|
||||||
|
|
||||||
this.WhenActivated(disposables =>
|
this.WhenActivated(disposables =>
|
||||||
{
|
{
|
||||||
this.OneWayBind(ViewModel, vm => vm.ProfileItems, v => v.lstProfiles.ItemsSource).DisposeWith(disposables);
|
this.OneWayBind(ViewModel, vm => vm.ProfileItems, v => v.lstProfiles.ItemsSource).DisposeWith(disposables);
|
||||||
|
|
@ -44,6 +41,8 @@ public partial class ProfilesSelectWindow
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSub, v => v.lstGroup.SelectedItem).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSub, v => v.lstGroup.SelectedItem).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.ServerFilter, v => v.txtServerFilter.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.ServerFilter, v => v.txtServerFilter.Text).DisposeWith(disposables);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AllowMultiSelect(bool allow)
|
public void AllowMultiSelect(bool allow)
|
||||||
|
|
@ -190,5 +189,6 @@ public partial class ProfilesSelectWindow
|
||||||
// Trigger selection finalize when Confirm is clicked
|
// Trigger selection finalize when Confirm is clicked
|
||||||
ViewModel?.SelectFinish();
|
ViewModel?.SelectFinish();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Event
|
#endregion Event
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ public partial class ProfilesView
|
||||||
if (obj is null)
|
if (obj is null)
|
||||||
return false;
|
return false;
|
||||||
WindowsUtils.SetClipboardData((string)obj);
|
WindowsUtils.SetClipboardData((string)obj);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EViewAction.ProfilesFocus:
|
case EViewAction.ProfilesFocus:
|
||||||
lstProfiles.Focus();
|
lstProfiles.Focus();
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
using System.Reactive.Disposables;
|
using System.Reactive.Disposables;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Threading;
|
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
using ServiceLib.Manager;
|
using ServiceLib.Manager;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue