diff --git a/v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs b/v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs index d932c5ef..44ab2b40 100644 --- a/v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs +++ b/v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs @@ -1,4 +1,4 @@ -namespace ServiceLib.Handler.Builder; +namespace ServiceLib.Handler.Builder; public record CoreConfigContextBuilderResult(CoreConfigContext Context, NodeValidatorResult ValidatorResult) { @@ -44,9 +44,17 @@ public class CoreConfigContextBuilder var rules = JsonUtils.Deserialize>(context.RoutingItem?.RuleSet); foreach (var ruleItem in rules.Where(ruleItem => !Global.OutboundTags.Contains(ruleItem.OutboundTag))) { + if (ruleItem.OutboundTag.IsNullOrEmpty()) + { + validatorResult.Warnings.Add(string.Format(ResUI.MsgRoutingRuleEmptyOutboundTag, ruleItem.Remarks)); + ruleItem.OutboundTag = Global.ProxyTag; + continue; + } var ruleOutboundNode = await AppManager.Instance.GetProfileItemViaRemarks(ruleItem.OutboundTag); if (ruleOutboundNode == null) { + validatorResult.Warnings.Add(string.Format(ResUI.MsgRoutingRuleOutboundNodeNotFound, ruleItem.Remarks, ruleItem.OutboundTag)); + ruleItem.OutboundTag = Global.ProxyTag; continue; } @@ -83,43 +91,71 @@ public class CoreConfigContextBuilder if (includeSubChain) { - var virtualChainNode = await BuildSubscriptionChainNodeAsync(node); + var (virtualChainNode, chainValidatorResult) = await BuildSubscriptionChainNodeAsync(node); if (virtualChainNode != null) { context.AllProxiesMap[virtualChainNode.IndexId] = virtualChainNode; - return await ResolveNodeAsync(context, virtualChainNode, false); + var (resolvedNode, resolvedResult) = await ResolveNodeAsync(context, virtualChainNode, false); + resolvedResult.Warnings.InsertRange(0, chainValidatorResult.Warnings); + return (resolvedNode, resolvedResult); + } + // Chain not built but warnings may still exist (e.g. missing profiles) + if (chainValidatorResult.Warnings.Count > 0) + { + var fillResult = await RegisterNodeAsync(context, node); + fillResult.Warnings.InsertRange(0, chainValidatorResult.Warnings); + return (node, fillResult); } } - var fillResult = await RegisterNodeAsync(context, node); - return (node, fillResult); + var registerResult = await RegisterNodeAsync(context, node); + return (node, registerResult); } /// /// If the node's subscription defines prev/next profiles, creates a virtual /// node that wraps them together. - /// Returns null when no chain is needed. + /// Returns null as the chain item when no chain is needed. + /// Any warnings (e.g. missing prev/next profile) are returned in the validator result. /// - private static async Task BuildSubscriptionChainNodeAsync(ProfileItem node) + private static async Task<(ProfileItem? ChainNode, NodeValidatorResult ValidatorResult)> BuildSubscriptionChainNodeAsync(ProfileItem node) { + var result = NodeValidatorResult.Empty(); + if (node.Subid.IsNullOrEmpty()) { - return null; + return (null, result); } var subItem = await AppManager.Instance.GetSubItem(node.Subid); - if (subItem == null - || (subItem.PrevProfile.IsNullOrEmpty() - && subItem.NextProfile.IsNullOrEmpty())) + if (subItem == null) { - return null; + return (null, result); + } + + ProfileItem? prevNode = null; + ProfileItem? nextNode = null; + + if (!subItem.PrevProfile.IsNullOrEmpty()) + { + prevNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.PrevProfile); + if (prevNode == null) + { + result.Warnings.Add(string.Format(ResUI.MsgSubscriptionPrevProfileNotFound, subItem.PrevProfile)); + } + } + if (!subItem.NextProfile.IsNullOrEmpty()) + { + nextNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.NextProfile); + if (nextNode == null) + { + result.Warnings.Add(string.Format(ResUI.MsgSubscriptionNextProfileNotFound, subItem.NextProfile)); + } } - var prevNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.PrevProfile); - var nextNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.NextProfile); if (prevNode is null && nextNode is null) { - return null; + return (null, result); } // Build new proxy chain node @@ -136,7 +172,7 @@ public class CoreConfigContextBuilder ChildItems = string.Join(",", childItems.Where(x => !x.IsNullOrEmpty())), }; chainNode.SetProtocolExtra(chainExtraItem); - return chainNode; + return (chainNode, result); } /// diff --git a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs index 10a1aed3..5e35d3bb 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs +++ b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs @@ -2040,6 +2040,15 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Routing rule {0} has an empty outbound tag. Fallback to proxy node only. 的本地化字符串。 + /// + public static string MsgRoutingRuleEmptyOutboundTag { + get { + return ResourceManager.GetString("MsgRoutingRuleEmptyOutboundTag", resourceCulture); + } + } + /// /// 查找类似 Routing rule {0} outbound node {1} error: {2}. Fallback to proxy node only. 的本地化字符串。 /// @@ -2049,6 +2058,15 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Routing rule {0} outbound node {1} not found. Fallback to proxy node only. 的本地化字符串。 + /// + public static string MsgRoutingRuleOutboundNodeNotFound { + get { + return ResourceManager.GetString("MsgRoutingRuleOutboundNodeNotFound", resourceCulture); + } + } + /// /// 查找类似 Routing rule {0} outbound node {1} warning: {2} 的本地化字符串。 /// @@ -2112,6 +2130,24 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Subscription next proxy {0} not found. Skipping. 的本地化字符串。 + /// + public static string MsgSubscriptionNextProfileNotFound { + get { + return ResourceManager.GetString("MsgSubscriptionNextProfileNotFound", resourceCulture); + } + } + + /// + /// 查找类似 Subscription previous proxy {0} not found. Skipping. 的本地化字符串。 + /// + public static string MsgSubscriptionPrevProfileNotFound { + get { + return ResourceManager.GetString("MsgSubscriptionPrevProfileNotFound", resourceCulture); + } + } + /// /// 查找类似 Unpacking... 的本地化字符串。 /// diff --git a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx index c8aec9ae..0df75d77 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx @@ -1674,4 +1674,16 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if Group {0} has no valid child node. + + Routing rule {0} has an empty outbound tag. Fallback to proxy node only. + + + Routing rule {0} outbound node {1} not found. Fallback to proxy node only. + + + Subscription previous proxy {0} not found. Skipping. + + + Subscription next proxy {0} not found. Skipping. + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.fr.resx b/v2rayN/ServiceLib/Resx/ResUI.fr.resx index f606225b..459e3b26 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fr.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fr.resx @@ -1671,4 +1671,16 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if Group {0} has no valid child node. + + Routing rule {0} has an empty outbound tag. Fallback to proxy node only. + + + Routing rule {0} outbound node {1} not found. Fallback to proxy node only. + + + Subscription previous proxy {0} not found. Skipping. + + + Subscription next proxy {0} not found. Skipping. + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.hu.resx b/v2rayN/ServiceLib/Resx/ResUI.hu.resx index 3eb3b357..dd7d22f4 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.hu.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.hu.resx @@ -1674,4 +1674,16 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if Group {0} has no valid child node. + + Routing rule {0} has an empty outbound tag. Fallback to proxy node only. + + + Routing rule {0} outbound node {1} not found. Fallback to proxy node only. + + + Subscription previous proxy {0} not found. Skipping. + + + Subscription next proxy {0} not found. Skipping. + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.resx b/v2rayN/ServiceLib/Resx/ResUI.resx index a2d9e691..fa53ccc8 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.resx @@ -1674,4 +1674,16 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if Group {0} has no valid child node. + + Routing rule {0} has an empty outbound tag. Fallback to proxy node only. + + + Routing rule {0} outbound node {1} not found. Fallback to proxy node only. + + + Subscription previous proxy {0} not found. Skipping. + + + Subscription next proxy {0} not found. Skipping. + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.ru.resx b/v2rayN/ServiceLib/Resx/ResUI.ru.resx index 4e851e20..b33a10e3 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.ru.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.ru.resx @@ -1674,4 +1674,16 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if Group {0} has no valid child node. + + Routing rule {0} has an empty outbound tag. Fallback to proxy node only. + + + Routing rule {0} outbound node {1} not found. Fallback to proxy node only. + + + Subscription previous proxy {0} not found. Skipping. + + + Subscription next proxy {0} not found. Skipping. + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx index 21e199ac..be76525e 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx @@ -1671,4 +1671,16 @@ 节点组 {0} 下没有有效的子节点。 + + 路由规则 {0} 的出站标签为空,已回退为仅使用代理节点。 + + + 路由规则 {0} 的出站节点 {1} 未找到,已回退为仅使用代理节点。 + + + 订阅前置节点 {0} 未找到,已跳过。 + + + 订阅后置节点 {0} 未找到,已跳过。 + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx index 5b74197c..4132a27b 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx @@ -1671,4 +1671,16 @@ Group {0} has no valid child node. + + Routing rule {0} has an empty outbound tag. Fallback to proxy node only. + + + Routing rule {0} outbound node {1} not found. Fallback to proxy node only. + + + Subscription previous proxy {0} not found. Skipping. + + + Subscription next proxy {0} not found. Skipping. + \ No newline at end of file