Add remark not found warnings

This commit is contained in:
DHR60 2026-02-27 10:39:09 +08:00
parent 07a5619e60
commit 50d8d99336
9 changed files with 172 additions and 16 deletions

View file

@ -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<List<RulesItem>>(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);
}
/// <summary>
/// If the node's subscription defines prev/next profiles, creates a virtual
/// <see cref="EConfigType.ProxyChain"/> node that wraps them together.
/// Returns <c>null</c> when no chain is needed.
/// Returns <c>null</c> as the chain item when no chain is needed.
/// Any warnings (e.g. missing prev/next profile) are returned in the validator result.
/// </summary>
private static async Task<ProfileItem?> 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);
}
/// <summary>

View file

@ -2040,6 +2040,15 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 Routing rule {0} has an empty outbound tag. Fallback to proxy node only. 的本地化字符串。
/// </summary>
public static string MsgRoutingRuleEmptyOutboundTag {
get {
return ResourceManager.GetString("MsgRoutingRuleEmptyOutboundTag", resourceCulture);
}
}
/// <summary>
/// 查找类似 Routing rule {0} outbound node {1} error: {2}. Fallback to proxy node only. 的本地化字符串。
/// </summary>
@ -2049,6 +2058,15 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 Routing rule {0} outbound node {1} not found. Fallback to proxy node only. 的本地化字符串。
/// </summary>
public static string MsgRoutingRuleOutboundNodeNotFound {
get {
return ResourceManager.GetString("MsgRoutingRuleOutboundNodeNotFound", resourceCulture);
}
}
/// <summary>
/// 查找类似 Routing rule {0} outbound node {1} warning: {2} 的本地化字符串。
/// </summary>
@ -2112,6 +2130,24 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 Subscription next proxy {0} not found. Skipping. 的本地化字符串。
/// </summary>
public static string MsgSubscriptionNextProfileNotFound {
get {
return ResourceManager.GetString("MsgSubscriptionNextProfileNotFound", resourceCulture);
}
}
/// <summary>
/// 查找类似 Subscription previous proxy {0} not found. Skipping. 的本地化字符串。
/// </summary>
public static string MsgSubscriptionPrevProfileNotFound {
get {
return ResourceManager.GetString("MsgSubscriptionPrevProfileNotFound", resourceCulture);
}
}
/// <summary>
/// 查找类似 Unpacking... 的本地化字符串。
/// </summary>

View file

@ -1674,4 +1674,16 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="MsgGroupNoValidChildNode" xml:space="preserve">
<value>Group {0} has no valid child node.</value>
</data>
<data name="MsgRoutingRuleEmptyOutboundTag" xml:space="preserve">
<value>Routing rule {0} has an empty outbound tag. Fallback to proxy node only.</value>
</data>
<data name="MsgRoutingRuleOutboundNodeNotFound" xml:space="preserve">
<value>Routing rule {0} outbound node {1} not found. Fallback to proxy node only.</value>
</data>
<data name="MsgSubscriptionPrevProfileNotFound" xml:space="preserve">
<value>Subscription previous proxy {0} not found. Skipping.</value>
</data>
<data name="MsgSubscriptionNextProfileNotFound" xml:space="preserve">
<value>Subscription next proxy {0} not found. Skipping.</value>
</data>
</root>

View file

@ -1671,4 +1671,16 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="MsgGroupNoValidChildNode" xml:space="preserve">
<value>Group {0} has no valid child node.</value>
</data>
<data name="MsgRoutingRuleEmptyOutboundTag" xml:space="preserve">
<value>Routing rule {0} has an empty outbound tag. Fallback to proxy node only.</value>
</data>
<data name="MsgRoutingRuleOutboundNodeNotFound" xml:space="preserve">
<value>Routing rule {0} outbound node {1} not found. Fallback to proxy node only.</value>
</data>
<data name="MsgSubscriptionPrevProfileNotFound" xml:space="preserve">
<value>Subscription previous proxy {0} not found. Skipping.</value>
</data>
<data name="MsgSubscriptionNextProfileNotFound" xml:space="preserve">
<value>Subscription next proxy {0} not found. Skipping.</value>
</data>
</root>

View file

@ -1674,4 +1674,16 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="MsgGroupNoValidChildNode" xml:space="preserve">
<value>Group {0} has no valid child node.</value>
</data>
<data name="MsgRoutingRuleEmptyOutboundTag" xml:space="preserve">
<value>Routing rule {0} has an empty outbound tag. Fallback to proxy node only.</value>
</data>
<data name="MsgRoutingRuleOutboundNodeNotFound" xml:space="preserve">
<value>Routing rule {0} outbound node {1} not found. Fallback to proxy node only.</value>
</data>
<data name="MsgSubscriptionPrevProfileNotFound" xml:space="preserve">
<value>Subscription previous proxy {0} not found. Skipping.</value>
</data>
<data name="MsgSubscriptionNextProfileNotFound" xml:space="preserve">
<value>Subscription next proxy {0} not found. Skipping.</value>
</data>
</root>

View file

@ -1674,4 +1674,16 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="MsgGroupNoValidChildNode" xml:space="preserve">
<value>Group {0} has no valid child node.</value>
</data>
<data name="MsgRoutingRuleEmptyOutboundTag" xml:space="preserve">
<value>Routing rule {0} has an empty outbound tag. Fallback to proxy node only.</value>
</data>
<data name="MsgRoutingRuleOutboundNodeNotFound" xml:space="preserve">
<value>Routing rule {0} outbound node {1} not found. Fallback to proxy node only.</value>
</data>
<data name="MsgSubscriptionPrevProfileNotFound" xml:space="preserve">
<value>Subscription previous proxy {0} not found. Skipping.</value>
</data>
<data name="MsgSubscriptionNextProfileNotFound" xml:space="preserve">
<value>Subscription next proxy {0} not found. Skipping.</value>
</data>
</root>

View file

@ -1674,4 +1674,16 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="MsgGroupNoValidChildNode" xml:space="preserve">
<value>Group {0} has no valid child node.</value>
</data>
<data name="MsgRoutingRuleEmptyOutboundTag" xml:space="preserve">
<value>Routing rule {0} has an empty outbound tag. Fallback to proxy node only.</value>
</data>
<data name="MsgRoutingRuleOutboundNodeNotFound" xml:space="preserve">
<value>Routing rule {0} outbound node {1} not found. Fallback to proxy node only.</value>
</data>
<data name="MsgSubscriptionPrevProfileNotFound" xml:space="preserve">
<value>Subscription previous proxy {0} not found. Skipping.</value>
</data>
<data name="MsgSubscriptionNextProfileNotFound" xml:space="preserve">
<value>Subscription next proxy {0} not found. Skipping.</value>
</data>
</root>

View file

@ -1671,4 +1671,16 @@
<data name="MsgGroupNoValidChildNode" xml:space="preserve">
<value>节点组 {0} 下没有有效的子节点。</value>
</data>
<data name="MsgRoutingRuleEmptyOutboundTag" xml:space="preserve">
<value>路由规则 {0} 的出站标签为空,已回退为仅使用代理节点。</value>
</data>
<data name="MsgRoutingRuleOutboundNodeNotFound" xml:space="preserve">
<value>路由规则 {0} 的出站节点 {1} 未找到,已回退为仅使用代理节点。</value>
</data>
<data name="MsgSubscriptionPrevProfileNotFound" xml:space="preserve">
<value>订阅前置节点 {0} 未找到,已跳过。</value>
</data>
<data name="MsgSubscriptionNextProfileNotFound" xml:space="preserve">
<value>订阅后置节点 {0} 未找到,已跳过。</value>
</data>
</root>

View file

@ -1671,4 +1671,16 @@
<data name="MsgGroupNoValidChildNode" xml:space="preserve">
<value>Group {0} has no valid child node.</value>
</data>
<data name="MsgRoutingRuleEmptyOutboundTag" xml:space="preserve">
<value>Routing rule {0} has an empty outbound tag. Fallback to proxy node only.</value>
</data>
<data name="MsgRoutingRuleOutboundNodeNotFound" xml:space="preserve">
<value>Routing rule {0} outbound node {1} not found. Fallback to proxy node only.</value>
</data>
<data name="MsgSubscriptionPrevProfileNotFound" xml:space="preserve">
<value>Subscription previous proxy {0} not found. Skipping.</value>
</data>
<data name="MsgSubscriptionNextProfileNotFound" xml:space="preserve">
<value>Subscription next proxy {0} not found. Skipping.</value>
</data>
</root>