Compare commits

..

24 commits

Author SHA1 Message Date
DHR60
ea857861d0
Merge 11924b545f into 5d6c5da9d9 2025-09-29 06:05:21 +08:00
DHR60
11924b545f PreCheck 2025-09-23 16:50:17 +08:00
DHR60
ab1dc45ed4 Fix 2025-09-23 16:49:52 +08:00
DHR60
2d41272659 Avoid self-reference 2025-09-23 12:46:59 +08:00
DHR60
e7f75010d3 Add chain selection control to group outbounds 2025-09-23 11:24:27 +08:00
DHR60
aa1ccdd01b Refactor 2025-09-23 11:24:17 +08:00
DHR60
b17323c982 Add helper function 2025-09-23 11:11:03 +08:00
DHR60
71dcd8d1de Adjust chained proxy, actual outbound is at the top
Based on actual network flow instead of data packets
2025-09-21 17:01:41 +08:00
DHR60
dace865e6c Refactor 2025-09-21 17:01:41 +08:00
DHR60
fad94e68d2 Avoid duplicate tags 2025-09-21 17:01:41 +08:00
DHR60
c116aae242 Add group in traffic splitting support 2025-09-21 17:01:41 +08:00
DHR60
ff1c9093a2 Add PolicyGroup include other Group support 2025-09-21 17:01:41 +08:00
DHR60
8bb20c0ab8 Add fallback support 2025-09-21 17:01:41 +08:00
DHR60
17a3a516c7 Fix 2025-09-21 17:01:41 +08:00
DHR60
8f0d7e54d8 Add Proxy Chain support 2025-09-21 17:01:41 +08:00
DHR60
2ab79afa02 Adjust UI 2025-09-21 17:01:41 +08:00
DHR60
e29d292732 Add generate policy group 2025-09-21 17:01:41 +08:00
DHR60
9ef228db1e Add Policy Group support 2025-09-21 17:01:41 +08:00
DHR60
34327532e6 Rename 2025-09-21 17:01:41 +08:00
DHR60
8af6eda165 Exclude specific profile types from selection 2025-09-21 17:01:41 +08:00
DHR60
f979d13109 Fix right click not working 2025-09-21 17:01:41 +08:00
DHR60
6166b6c0e3 avalonia 2025-09-21 17:01:40 +08:00
DHR60
8c094dd976 VM and wpf 2025-09-21 17:01:40 +08:00
DHR60
5c4f485471 Multi Profile 2025-09-21 17:01:40 +08:00
6 changed files with 17 additions and 105 deletions

View file

@ -1253,49 +1253,12 @@ public static class ConfigHandler
ProfileItem? itemSocks = null; ProfileItem? itemSocks = null;
if (node.ConfigType != EConfigType.Custom && coreType != ECoreType.sing_box && config.TunModeItem.EnableTun) if (node.ConfigType != EConfigType.Custom && coreType != ECoreType.sing_box && config.TunModeItem.EnableTun)
{ {
var tun2SocksAddress = node.Address;
if (node.ConfigType > EConfigType.Group)
{
static async Task<List<string>> GetChildNodeAddressesAsync(string parentIndexId)
{
var childAddresses = new List<string>();
if (!ProfileGroupItemManager.Instance.TryGet(parentIndexId, out var groupItem) || groupItem.ChildItems.IsNullOrEmpty())
return childAddresses;
var childIds = Utils.String2List(groupItem.ChildItems);
foreach (var childId in childIds)
{
var childNode = await AppManager.Instance.GetProfileItem(childId);
if (childNode == null)
continue;
if (!childNode.IsComplex())
{
childAddresses.Add(childNode.Address);
}
else if (childNode.ConfigType > EConfigType.Group)
{
var subAddresses = await GetChildNodeAddressesAsync(childNode.IndexId);
childAddresses.AddRange(subAddresses);
}
}
return childAddresses;
}
var lstAddresses = await GetChildNodeAddressesAsync(node.IndexId);
if (lstAddresses.Count > 0)
{
tun2SocksAddress = Utils.List2String(lstAddresses);
}
}
itemSocks = new ProfileItem() itemSocks = new ProfileItem()
{ {
CoreType = ECoreType.sing_box, CoreType = ECoreType.sing_box,
ConfigType = EConfigType.SOCKS, ConfigType = EConfigType.SOCKS,
Address = Global.Loopback, Address = Global.Loopback,
SpiderX = tun2SocksAddress, // Tun2SocksAddress SpiderX = node.Address, // Tun2SocksAddress
Port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks) Port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks)
}; };
} }

View file

@ -109,42 +109,6 @@ public class ProfileItem : ReactiveObject
return true; return true;
} }
public async Task<bool> HasCycle(HashSet<string> visited, HashSet<string> stack)
{
if (ConfigType < EConfigType.Group)
return false;
if (stack.Contains(IndexId))
return true;
if (visited.Contains(IndexId))
return false;
visited.Add(IndexId);
stack.Add(IndexId);
if (ProfileGroupItemManager.Instance.TryGet(IndexId, out var group)
&& !group.ChildItems.IsNullOrEmpty())
{
var childProfiles = (await Task.WhenAll(
Utils.String2List(group.ChildItems)
.Where(p => !p.IsNullOrEmpty())
.Select(AppManager.Instance.GetProfileItem)
))
.Where(p => p != null)
.ToList();
foreach (var child in childProfiles)
{
if (await child.HasCycle(visited, stack))
return true;
}
}
stack.Remove(IndexId);
return false;
}
#endregion function #endregion function
[PrimaryKey] [PrimaryKey]

View file

@ -123,14 +123,7 @@ public class ActionPrecheckService(Config config)
errors.Add(string.Format(ResUI.GroupEmpty, item.Remarks)); errors.Add(string.Format(ResUI.GroupEmpty, item.Remarks));
return errors; return errors;
} }
var hasCycle = await item.HasCycle(new HashSet<string>(), new HashSet<string>());
if (hasCycle)
{
errors.Add(string.Format(ResUI.GroupSelfReference, item.Remarks));
return errors;
}
foreach (var child in Utils.String2List(group.ChildItems)) foreach (var child in Utils.String2List(group.ChildItems))
{ {
var childErrors = new List<string>(); var childErrors = new List<string>();
@ -146,6 +139,13 @@ public class ActionPrecheckService(Config config)
continue; continue;
} }
// self-reference check
if (childItem.IndexId == item.IndexId)
{
childErrors.Add(string.Format(ResUI.GroupSelfReference, childItem.Remarks));
continue;
}
if (childItem.ConfigType is EConfigType.Custom or EConfigType.ProxyChain) if (childItem.ConfigType is EConfigType.Custom or EConfigType.ProxyChain)
{ {
childErrors.Add(string.Format(ResUI.InvalidProperty, childItem.Remarks)); childErrors.Add(string.Format(ResUI.InvalidProperty, childItem.Remarks));

View file

@ -414,19 +414,16 @@ public partial class CoreConfigSingboxService
return 0; return 0;
} }
List<string> domain = new(); var domain = string.Empty;
if (Utils.IsDomain(node.Address)) // normal outbound if (Utils.IsDomain(node.Address)) // normal outbound
{ {
domain.Add(node.Address); domain = node.Address;
} }
if (node.Address == Global.Loopback && node.SpiderX.IsNotEmpty()) // Tun2SocksAddress else if (node.Address == Global.Loopback && node.SpiderX.IsNotEmpty() && Utils.IsDomain(node.SpiderX)) // Tun2SocksAddress
{ {
domain.AddRange(Utils.String2List(node.SpiderX) domain = node.SpiderX;
.Where(Utils.IsDomain)
.Distinct()
.ToList());
} }
if (domain.Count == 0) if (domain.IsNullOrEmpty())
{ {
return 0; return 0;
} }
@ -435,7 +432,7 @@ public partial class CoreConfigSingboxService
singboxConfig.dns.rules.Insert(0, new Rule4Sbox singboxConfig.dns.rules.Insert(0, new Rule4Sbox
{ {
server = server, server = server,
domain = domain, domain = [domain],
}); });
return await Task.FromResult(0); return await Task.FromResult(0);

View file

@ -217,15 +217,9 @@ public partial class CoreConfigSingboxService
{ {
return -1; return -1;
} }
var hasCycle = await node.HasCycle(new HashSet<string>(), new HashSet<string>());
if (hasCycle)
{
return -1;
}
// remove custom nodes // remove custom nodes
// remove group nodes for proxy chain // remove group nodes for proxy chain
// avoid self-reference
var childProfiles = (await Task.WhenAll( var childProfiles = (await Task.WhenAll(
Utils.String2List(profileGroupItem.ChildItems) Utils.String2List(profileGroupItem.ChildItems)
.Where(p => !p.IsNullOrEmpty()) .Where(p => !p.IsNullOrEmpty())
@ -236,6 +230,7 @@ public partial class CoreConfigSingboxService
&& p.IsValid() && p.IsValid()
&& p.ConfigType != EConfigType.Custom && p.ConfigType != EConfigType.Custom
&& (node.ConfigType == EConfigType.PolicyGroup || p.ConfigType < EConfigType.Group) && (node.ConfigType == EConfigType.PolicyGroup || p.ConfigType < EConfigType.Group)
&& p.IndexId != node.IndexId
) )
.ToList(); .ToList();

View file

@ -493,13 +493,6 @@ public partial class CoreConfigV2rayService
{ {
return -1; return -1;
} }
var hasCycle = await node.HasCycle(new HashSet<string>(), new HashSet<string>());
if (hasCycle)
{
return -1;
}
// remove custom nodes // remove custom nodes
// remove group nodes for proxy chain // remove group nodes for proxy chain
var childProfiles = (await Task.WhenAll( var childProfiles = (await Task.WhenAll(