mirror of
https://github.com/2dust/v2rayN.git
synced 2026-02-28 13:13:04 +00:00
Refactor
This commit is contained in:
parent
fdb733fa72
commit
a6b12aa718
18 changed files with 388 additions and 496 deletions
|
|
@ -258,6 +258,7 @@ public static class ConfigHandler
|
||||||
item.CertSha = profileItem.CertSha;
|
item.CertSha = profileItem.CertSha;
|
||||||
item.EchConfigList = profileItem.EchConfigList;
|
item.EchConfigList = profileItem.EchConfigList;
|
||||||
item.EchForceQuery = profileItem.EchForceQuery;
|
item.EchForceQuery = profileItem.EchForceQuery;
|
||||||
|
item.JsonData = profileItem.JsonData;
|
||||||
}
|
}
|
||||||
|
|
||||||
var ret = item.ConfigType switch
|
var ret = item.ConfigType switch
|
||||||
|
|
@ -360,11 +361,6 @@ public static class ConfigHandler
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (profileItem.ConfigType.IsGroupType())
|
|
||||||
{
|
|
||||||
var profileGroupItem = await AppManager.Instance.GetProfileGroupItem(it.IndexId);
|
|
||||||
await AddGroupServerCommon(config, profileItem, profileGroupItem, true);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await AddServerCommon(config, profileItem, true);
|
await AddServerCommon(config, profileItem, true);
|
||||||
|
|
@ -967,9 +963,11 @@ public static class ConfigHandler
|
||||||
profileItem.Path = profileItem.Path.TrimEx();
|
profileItem.Path = profileItem.Path.TrimEx();
|
||||||
profileItem.StreamSecurity = profileItem.StreamSecurity.TrimEx();
|
profileItem.StreamSecurity = profileItem.StreamSecurity.TrimEx();
|
||||||
|
|
||||||
if (!Global.Flows.Contains(profileItem.Flow))
|
var extraItem = profileItem.GetExtraItem();
|
||||||
|
|
||||||
|
if (!Global.Flows.Contains(extraItem.Flow ?? string.Empty))
|
||||||
{
|
{
|
||||||
profileItem.Flow = Global.Flows.First();
|
extraItem.Flow = Global.Flows.First();
|
||||||
}
|
}
|
||||||
if (profileItem.Id.IsNullOrEmpty())
|
if (profileItem.Id.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
|
|
@ -980,6 +978,8 @@ public static class ConfigHandler
|
||||||
profileItem.Security = Global.None;
|
profileItem.Security = Global.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
profileItem.SetExtraItem(extraItem);
|
||||||
|
|
||||||
await AddServerCommon(config, profileItem, toFile);
|
await AddServerCommon(config, profileItem, toFile);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -1082,37 +1082,6 @@ public static class ConfigHandler
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<int> AddGroupServerCommon(Config config, ProfileItem profileItem, ProfileGroupItem profileGroupItem, bool toFile = true)
|
|
||||||
{
|
|
||||||
var maxSort = -1;
|
|
||||||
if (profileItem.IndexId.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
profileItem.IndexId = Utils.GetGuid(false);
|
|
||||||
maxSort = ProfileExManager.Instance.GetMaxSort();
|
|
||||||
}
|
|
||||||
var groupType = profileItem.ConfigType == EConfigType.ProxyChain ? EConfigType.ProxyChain.ToString() : profileGroupItem.MultipleLoad.ToString();
|
|
||||||
profileItem.Address = $"{profileItem.CoreType}-{groupType}";
|
|
||||||
if (maxSort > 0)
|
|
||||||
{
|
|
||||||
ProfileExManager.Instance.SetSort(profileItem.IndexId, maxSort + 1);
|
|
||||||
}
|
|
||||||
if (toFile)
|
|
||||||
{
|
|
||||||
await SQLiteHelper.Instance.ReplaceAsync(profileItem);
|
|
||||||
if (profileGroupItem != null)
|
|
||||||
{
|
|
||||||
profileGroupItem.IndexId = profileItem.IndexId;
|
|
||||||
await ProfileGroupItemManager.Instance.SaveItemAsync(profileGroupItem);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ProfileGroupItemManager.Instance.GetOrCreateAndMarkDirty(profileItem.IndexId);
|
|
||||||
await ProfileGroupItemManager.Instance.SaveTo();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Compare two profile items to determine if they represent the same server
|
/// Compare two profile items to determine if they represent the same server
|
||||||
/// Used for deduplication and server matching
|
/// Used for deduplication and server matching
|
||||||
|
|
@ -1128,6 +1097,8 @@ public static class ConfigHandler
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var extraItem = o.GetExtraItem();
|
||||||
|
|
||||||
return o.ConfigType == n.ConfigType
|
return o.ConfigType == n.ConfigType
|
||||||
&& AreEqual(o.Address, n.Address)
|
&& AreEqual(o.Address, n.Address)
|
||||||
&& o.Port == n.Port
|
&& o.Port == n.Port
|
||||||
|
|
@ -1138,7 +1109,7 @@ public static class ConfigHandler
|
||||||
&& AreEqual(o.RequestHost, n.RequestHost)
|
&& AreEqual(o.RequestHost, n.RequestHost)
|
||||||
&& AreEqual(o.Path, n.Path)
|
&& AreEqual(o.Path, n.Path)
|
||||||
&& (o.ConfigType == EConfigType.Trojan || o.StreamSecurity == n.StreamSecurity)
|
&& (o.ConfigType == EConfigType.Trojan || o.StreamSecurity == n.StreamSecurity)
|
||||||
&& AreEqual(o.Flow, n.Flow)
|
&& AreEqual(extraItem.Flow, extraItem.Flow)
|
||||||
&& AreEqual(o.Sni, n.Sni)
|
&& AreEqual(o.Sni, n.Sni)
|
||||||
&& AreEqual(o.Alpn, n.Alpn)
|
&& AreEqual(o.Alpn, n.Alpn)
|
||||||
&& AreEqual(o.Fingerprint, n.Fingerprint)
|
&& AreEqual(o.Fingerprint, n.Fingerprint)
|
||||||
|
|
@ -1199,7 +1170,7 @@ public static class ConfigHandler
|
||||||
var indexId = Utils.GetGuid(false);
|
var indexId = Utils.GetGuid(false);
|
||||||
var childProfileIndexId = Utils.List2String(selecteds.Select(p => p.IndexId).ToList());
|
var childProfileIndexId = Utils.List2String(selecteds.Select(p => p.IndexId).ToList());
|
||||||
|
|
||||||
var remark = subId.IsNullOrEmpty() ? string.Empty : $"{(await AppManager.Instance.GetSubItem(subId)).Remarks} ";
|
var remark = subId.IsNullOrEmpty() ? string.Empty : $"{(await AppManager.Instance.GetSubItem(subId))?.Remarks} ";
|
||||||
if (coreType == ECoreType.Xray)
|
if (coreType == ECoreType.Xray)
|
||||||
{
|
{
|
||||||
remark += multipleLoad switch
|
remark += multipleLoad switch
|
||||||
|
|
@ -1233,13 +1204,12 @@ public static class ConfigHandler
|
||||||
{
|
{
|
||||||
profile.Subid = subId;
|
profile.Subid = subId;
|
||||||
}
|
}
|
||||||
var profileGroup = new ProfileGroupItem
|
var extraItem = new ProtocolExtraItem
|
||||||
{
|
{
|
||||||
ChildItems = childProfileIndexId,
|
ChildItems = childProfileIndexId, MultipleLoad = multipleLoad,
|
||||||
MultipleLoad = multipleLoad,
|
|
||||||
IndexId = indexId,
|
|
||||||
};
|
};
|
||||||
var ret = await AddGroupServerCommon(config, profile, profileGroup, true);
|
profile.SetExtraItem(extraItem);
|
||||||
|
var ret = await AddServerCommon(config, profile, true);
|
||||||
result.Success = ret == 0;
|
result.Success = ret == 0;
|
||||||
result.Data = indexId;
|
result.Data = indexId;
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -1261,7 +1231,7 @@ public static class ConfigHandler
|
||||||
var tun2SocksAddress = node.Address;
|
var tun2SocksAddress = node.Address;
|
||||||
if (node.ConfigType.IsGroupType())
|
if (node.ConfigType.IsGroupType())
|
||||||
{
|
{
|
||||||
var lstAddresses = (await ProfileGroupItemManager.GetAllChildDomainAddresses(node.IndexId)).ToList();
|
var lstAddresses = (await GroupProfileManager.GetAllChildDomainAddresses(node)).ToList();
|
||||||
if (lstAddresses.Count > 0)
|
if (lstAddresses.Count > 0)
|
||||||
{
|
{
|
||||||
tun2SocksAddress = Utils.List2String(lstAddresses);
|
tun2SocksAddress = Utils.List2String(lstAddresses);
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,10 @@ public class BaseFmt
|
||||||
|
|
||||||
protected static int ToUriQuery(ProfileItem item, string? securityDef, ref Dictionary<string, string> dicQuery)
|
protected static int ToUriQuery(ProfileItem item, string? securityDef, ref Dictionary<string, string> dicQuery)
|
||||||
{
|
{
|
||||||
if (item.Flow.IsNotEmpty())
|
var extraItem = item.GetExtraItem();
|
||||||
|
if (!extraItem.Flow.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
dicQuery.Add("flow", item.Flow);
|
dicQuery.Add("flow", extraItem.Flow);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.StreamSecurity.IsNotEmpty())
|
if (item.StreamSecurity.IsNotEmpty())
|
||||||
|
|
@ -208,7 +209,9 @@ public class BaseFmt
|
||||||
|
|
||||||
protected static int ResolveUriQuery(NameValueCollection query, ref ProfileItem item)
|
protected static int ResolveUriQuery(NameValueCollection query, ref ProfileItem item)
|
||||||
{
|
{
|
||||||
item.Flow = GetQueryValue(query, "flow");
|
var extraItem = item.GetExtraItem();
|
||||||
|
extraItem.Flow = GetQueryValue(query, "flow");
|
||||||
|
item.SetExtraItem(extraItem);
|
||||||
item.StreamSecurity = GetQueryValue(query, "security");
|
item.StreamSecurity = GetQueryValue(query, "security");
|
||||||
item.Sni = GetQueryValue(query, "sni");
|
item.Sni = GetQueryValue(query, "sni");
|
||||||
item.Alpn = GetQueryDecoded(query, "alpn");
|
item.Alpn = GetQueryDecoded(query, "alpn");
|
||||||
|
|
|
||||||
|
|
@ -24,11 +24,15 @@ public class Hysteria2Fmt : BaseFmt
|
||||||
var query = Utils.ParseQueryString(url.Query);
|
var query = Utils.ParseQueryString(url.Query);
|
||||||
ResolveUriQuery(query, ref item);
|
ResolveUriQuery(query, ref item);
|
||||||
item.Path = GetQueryDecoded(query, "obfs-password");
|
item.Path = GetQueryDecoded(query, "obfs-password");
|
||||||
item.Ports = GetQueryDecoded(query, "mport");
|
|
||||||
if (item.CertSha.IsNullOrEmpty())
|
if (item.CertSha.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
item.CertSha = GetQueryDecoded(query, "pinSHA256");
|
item.CertSha = GetQueryDecoded(query, "pinSHA256");
|
||||||
}
|
}
|
||||||
|
ProtocolExtraItem extraItem = new()
|
||||||
|
{
|
||||||
|
Ports = GetQueryDecoded(query, "mport")
|
||||||
|
};
|
||||||
|
item.SetExtraItem(extraItem);
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
@ -55,9 +59,10 @@ public class Hysteria2Fmt : BaseFmt
|
||||||
dicQuery.Add("obfs", "salamander");
|
dicQuery.Add("obfs", "salamander");
|
||||||
dicQuery.Add("obfs-password", Utils.UrlEncode(item.Path));
|
dicQuery.Add("obfs-password", Utils.UrlEncode(item.Path));
|
||||||
}
|
}
|
||||||
if (item.Ports.IsNotEmpty())
|
var extra = item.GetExtraItem();
|
||||||
|
if (extra?.Ports?.IsNotEmpty() ?? false)
|
||||||
{
|
{
|
||||||
dicQuery.Add("mport", Utils.UrlEncode(item.Ports.Replace(':', '-')));
|
dicQuery.Add("mport", Utils.UrlEncode(extra.Ports.Replace(':', '-')));
|
||||||
}
|
}
|
||||||
if (!item.CertSha.IsNullOrEmpty())
|
if (!item.CertSha.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@ public class VmessFmt : BaseFmt
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var extraItem = item?.GetExtraItem();
|
||||||
var vmessQRCode = new VmessQRCode
|
var vmessQRCode = new VmessQRCode
|
||||||
{
|
{
|
||||||
v = item.ConfigVersion,
|
v = item.ConfigVersion,
|
||||||
|
|
@ -30,7 +32,7 @@ public class VmessFmt : BaseFmt
|
||||||
add = item.Address,
|
add = item.Address,
|
||||||
port = item.Port,
|
port = item.Port,
|
||||||
id = item.Id,
|
id = item.Id,
|
||||||
aid = item.AlterId,
|
aid = int.TryParse(extraItem?.AlterId, out var result) ? result : 0,
|
||||||
scy = item.Security,
|
scy = item.Security,
|
||||||
net = item.Network,
|
net = item.Network,
|
||||||
type = item.HeaderType,
|
type = item.HeaderType,
|
||||||
|
|
@ -76,7 +78,10 @@ public class VmessFmt : BaseFmt
|
||||||
item.Address = Utils.ToString(vmessQRCode.add);
|
item.Address = Utils.ToString(vmessQRCode.add);
|
||||||
item.Port = vmessQRCode.port;
|
item.Port = vmessQRCode.port;
|
||||||
item.Id = Utils.ToString(vmessQRCode.id);
|
item.Id = Utils.ToString(vmessQRCode.id);
|
||||||
item.AlterId = vmessQRCode.aid;
|
item.SetExtraItem(new ProtocolExtraItem
|
||||||
|
{
|
||||||
|
AlterId = vmessQRCode.aid.ToString(),
|
||||||
|
});
|
||||||
item.Security = Utils.ToString(vmessQRCode.scy);
|
item.Security = Utils.ToString(vmessQRCode.scy);
|
||||||
|
|
||||||
item.Security = vmessQRCode.scy.IsNotEmpty() ? vmessQRCode.scy : Global.DefaultSecurity;
|
item.Security = vmessQRCode.scy.IsNotEmpty() ? vmessQRCode.scy : Global.DefaultSecurity;
|
||||||
|
|
|
||||||
|
|
@ -128,6 +128,8 @@ public class ActionPrecheckManager
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var extraItem = item.GetExtraItem();
|
||||||
|
|
||||||
switch (item.ConfigType)
|
switch (item.ConfigType)
|
||||||
{
|
{
|
||||||
case EConfigType.VMess:
|
case EConfigType.VMess:
|
||||||
|
|
@ -144,7 +146,7 @@ public class ActionPrecheckManager
|
||||||
errors.Add(string.Format(ResUI.InvalidProperty, "Id"));
|
errors.Add(string.Format(ResUI.InvalidProperty, "Id"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Global.Flows.Contains(item.Flow))
|
if (!Global.Flows.Contains(extraItem.Flow ?? string.Empty))
|
||||||
{
|
{
|
||||||
errors.Add(string.Format(ResUI.InvalidProperty, "Flow"));
|
errors.Add(string.Format(ResUI.InvalidProperty, "Flow"));
|
||||||
}
|
}
|
||||||
|
|
@ -202,37 +204,22 @@ public class ActionPrecheckManager
|
||||||
{
|
{
|
||||||
var errors = new List<string>();
|
var errors = new List<string>();
|
||||||
|
|
||||||
ProfileGroupItemManager.Instance.TryGet(item.IndexId, out var group);
|
var hasCycle = await GroupProfileManager.HasCycle(item.IndexId, item.GetExtraItem());
|
||||||
if (group is null || group.NotHasChild())
|
|
||||||
{
|
|
||||||
errors.Add(string.Format(ResUI.GroupEmpty, item.Remarks));
|
|
||||||
return errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
var hasCycle = ProfileGroupItemManager.HasCycle(item.IndexId);
|
|
||||||
if (hasCycle)
|
if (hasCycle)
|
||||||
{
|
{
|
||||||
errors.Add(string.Format(ResUI.GroupSelfReference, item.Remarks));
|
errors.Add(string.Format(ResUI.GroupSelfReference, item.Remarks));
|
||||||
return errors;
|
return errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
var childIds = new List<string>();
|
var (childItems, _) = await GroupProfileManager.GetChildProfileItems(item);
|
||||||
var subItems = await ProfileGroupItemManager.GetSubChildProfileItems(group);
|
|
||||||
childIds.AddRangeSafe(subItems.Select(p => p.IndexId));
|
|
||||||
childIds.AddRangeSafe(Utils.String2List(group.ChildItems));
|
|
||||||
|
|
||||||
foreach (var child in childIds)
|
foreach (var childItem in childItems)
|
||||||
{
|
{
|
||||||
var childErrors = new List<string>();
|
var childErrors = new List<string>();
|
||||||
if (child.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var childItem = await AppManager.Instance.GetProfileItem(child);
|
|
||||||
if (childItem is null)
|
if (childItem is null)
|
||||||
{
|
{
|
||||||
childErrors.Add(string.Format(ResUI.NodeTagNotExist, child));
|
childErrors.Add(string.Format(ResUI.NodeTagNotExist, ""));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,8 @@ public sealed class AppManager
|
||||||
_ = StatePort;
|
_ = StatePort;
|
||||||
_ = StatePort2;
|
_ = StatePort2;
|
||||||
|
|
||||||
|
_ = MigrateProfileExtra();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -225,15 +227,6 @@ public sealed class AppManager
|
||||||
return await SQLiteHelper.Instance.TableAsync<ProfileItem>().FirstOrDefaultAsync(it => it.Remarks == remarks);
|
return await SQLiteHelper.Instance.TableAsync<ProfileItem>().FirstOrDefaultAsync(it => it.Remarks == remarks);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ProfileGroupItem?> GetProfileGroupItem(string indexId)
|
|
||||||
{
|
|
||||||
if (indexId.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return await SQLiteHelper.Instance.TableAsync<ProfileGroupItem>().FirstOrDefaultAsync(it => it.IndexId == indexId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<List<RoutingItem>?> RoutingItems()
|
public async Task<List<RoutingItem>?> RoutingItems()
|
||||||
{
|
{
|
||||||
return await SQLiteHelper.Instance.TableAsync<RoutingItem>().OrderBy(t => t.Sort).ToListAsync();
|
return await SQLiteHelper.Instance.TableAsync<RoutingItem>().OrderBy(t => t.Sort).ToListAsync();
|
||||||
|
|
@ -264,6 +257,40 @@ public sealed class AppManager
|
||||||
return await SQLiteHelper.Instance.TableAsync<FullConfigTemplateItem>().FirstOrDefaultAsync(it => it.CoreType == eCoreType);
|
return await SQLiteHelper.Instance.TableAsync<FullConfigTemplateItem>().FirstOrDefaultAsync(it => it.CoreType == eCoreType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task MigrateProfileExtra()
|
||||||
|
{
|
||||||
|
var list = await SQLiteHelper.Instance.TableAsync<ProfileItem>().ToListAsync();
|
||||||
|
foreach (var item in list)
|
||||||
|
{
|
||||||
|
if (!item.JsonData.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ProtocolExtraItem extra = new()
|
||||||
|
{
|
||||||
|
AlterId = item.AlterId.ToString(),
|
||||||
|
Flow = item.Flow.IsNotEmpty() ? item.Flow : null,
|
||||||
|
Ports = item.Ports.IsNotEmpty() ? item.Ports : null,
|
||||||
|
};
|
||||||
|
if (item.ConfigType is EConfigType.PolicyGroup or EConfigType.ProxyChain)
|
||||||
|
{
|
||||||
|
extra.GroupType = nameof(item.ConfigType);
|
||||||
|
ProfileGroupItemManager.Instance.TryGet(item.IndexId, out var groupItem);
|
||||||
|
if (groupItem != null && !groupItem.NotHasChild())
|
||||||
|
{
|
||||||
|
extra.ChildItems = groupItem.ChildItems;
|
||||||
|
extra.SubChildItems = groupItem.SubChildItems;
|
||||||
|
extra.Filter = groupItem.Filter;
|
||||||
|
extra.MultipleLoad = groupItem.MultipleLoad;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
item.SetExtraItem(extra);
|
||||||
|
await SQLiteHelper.Instance.UpdateAsync(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
await ProfileGroupItemManager.Instance.ClearAll();
|
||||||
|
}
|
||||||
|
|
||||||
#endregion SqliteHelper
|
#endregion SqliteHelper
|
||||||
|
|
||||||
#region Core Type
|
#region Core Type
|
||||||
|
|
|
||||||
175
v2rayN/ServiceLib/Manager/GroupProfileManager.cs
Normal file
175
v2rayN/ServiceLib/Manager/GroupProfileManager.cs
Normal file
|
|
@ -0,0 +1,175 @@
|
||||||
|
namespace ServiceLib.Manager;
|
||||||
|
|
||||||
|
public class GroupProfileManager
|
||||||
|
{
|
||||||
|
public static async Task<bool> HasCycle(ProfileItem item)
|
||||||
|
{
|
||||||
|
return await HasCycle(item.IndexId, item.GetExtraItem());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<bool> HasCycle(string? indexId, ProtocolExtraItem? extraInfo)
|
||||||
|
{
|
||||||
|
return await HasCycle(indexId, extraInfo, new HashSet<string>(), new HashSet<string>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<bool> HasCycle(string? indexId, ProtocolExtraItem? extraInfo, HashSet<string> visited, HashSet<string> stack)
|
||||||
|
{
|
||||||
|
if (indexId.IsNullOrEmpty() || extraInfo == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stack.Contains(indexId))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (visited.Contains(indexId))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
visited.Add(indexId);
|
||||||
|
stack.Add(indexId);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (extraInfo.GroupType.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extraInfo.ChildItems.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var childIds = Utils.String2List(extraInfo.ChildItems)
|
||||||
|
?.Where(p => !string.IsNullOrEmpty(p))
|
||||||
|
.ToList();
|
||||||
|
if (childIds == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var child in childIds)
|
||||||
|
{
|
||||||
|
var childItem = await AppManager.Instance.GetProfileItem(child);
|
||||||
|
if (await HasCycle(child, childItem?.GetExtraItem(), visited, stack))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
stack.Remove(indexId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<(List<ProfileItem> Items, ProtocolExtraItem? Extra)> GetChildProfileItems(ProfileItem profileItem)
|
||||||
|
{
|
||||||
|
var profileExtra = profileItem?.GetExtraItem();
|
||||||
|
if (profileExtra == null)
|
||||||
|
{
|
||||||
|
return ([], null);
|
||||||
|
}
|
||||||
|
var items = await GetChildProfileItems(profileExtra);
|
||||||
|
var subItems = await GetSubChildProfileItems(profileExtra);
|
||||||
|
items.AddRange(subItems);
|
||||||
|
|
||||||
|
return (items, profileExtra);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<List<ProfileItem>> GetChildProfileItems(ProtocolExtraItem? extra)
|
||||||
|
{
|
||||||
|
if (extra == null || extra.ChildItems.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
return new();
|
||||||
|
}
|
||||||
|
var childProfiles = (await Task.WhenAll(
|
||||||
|
Utils.String2List(extra.ChildItems)
|
||||||
|
.Where(p => !p.IsNullOrEmpty())
|
||||||
|
.Select(AppManager.Instance.GetProfileItem)
|
||||||
|
))
|
||||||
|
.Where(p =>
|
||||||
|
p != null &&
|
||||||
|
p.IsValid() &&
|
||||||
|
p.ConfigType != EConfigType.Custom
|
||||||
|
)
|
||||||
|
.ToList();
|
||||||
|
return childProfiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<List<ProfileItem>> GetSubChildProfileItems(ProtocolExtraItem? extra)
|
||||||
|
{
|
||||||
|
if (extra == null || extra.Filter.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
return new();
|
||||||
|
}
|
||||||
|
var childProfiles = await AppManager.Instance.ProfileItems(extra.SubChildItems ?? string.Empty);
|
||||||
|
|
||||||
|
return childProfiles.Where(p =>
|
||||||
|
p != null &&
|
||||||
|
p.IsValid() &&
|
||||||
|
!p.ConfigType.IsComplexType() &&
|
||||||
|
(extra.Filter.IsNullOrEmpty() || Regex.IsMatch(p.Remarks, extra.Filter))
|
||||||
|
)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<HashSet<string>> GetAllChildDomainAddresses(ProfileItem profileItem)
|
||||||
|
{
|
||||||
|
var childAddresses = new HashSet<string>();
|
||||||
|
var (childItems, _) = await GetChildProfileItems(profileItem);
|
||||||
|
foreach (var child in childItems)
|
||||||
|
{
|
||||||
|
if (!child.IsComplex())
|
||||||
|
{
|
||||||
|
childAddresses.Add(child.Address);
|
||||||
|
}
|
||||||
|
else if (child.ConfigType.IsGroupType())
|
||||||
|
{
|
||||||
|
var subAddresses = await GetAllChildDomainAddresses(child);
|
||||||
|
foreach (var addr in subAddresses)
|
||||||
|
{
|
||||||
|
childAddresses.Add(addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return childAddresses;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<HashSet<string>> GetAllChildEchQuerySni(ProfileItem profileItem)
|
||||||
|
{
|
||||||
|
var childAddresses = new HashSet<string>();
|
||||||
|
var (childItems, _) = await GetChildProfileItems(profileItem);
|
||||||
|
foreach (var childNode in childItems)
|
||||||
|
{
|
||||||
|
if (!childNode.IsComplex() && !childNode.EchConfigList.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
if (childNode.StreamSecurity == Global.StreamSecurity
|
||||||
|
&& childNode.EchConfigList?.Contains("://") == true)
|
||||||
|
{
|
||||||
|
var idx = childNode.EchConfigList.IndexOf('+');
|
||||||
|
childAddresses.Add(idx > 0 ? childNode.EchConfigList[..idx] : childNode.Sni);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
childAddresses.Add(childNode.Sni);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (childNode.ConfigType.IsGroupType())
|
||||||
|
{
|
||||||
|
var subAddresses = await GetAllChildDomainAddresses(childNode);
|
||||||
|
foreach (var addr in subAddresses)
|
||||||
|
{
|
||||||
|
childAddresses.Add(addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return childAddresses;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -29,11 +29,6 @@ public class ProfileGroupItemManager
|
||||||
return _items.TryGetValue(indexId, out item);
|
return _items.TryGetValue(indexId, out item);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProfileGroupItem? GetOrDefault(string indexId)
|
|
||||||
{
|
|
||||||
return string.IsNullOrWhiteSpace(indexId) ? null : (_items.TryGetValue(indexId, out var v) ? v : null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task InitData()
|
private async Task InitData()
|
||||||
{
|
{
|
||||||
await SQLiteHelper.Instance.ExecuteAsync($"delete from ProfileGroupItem where IndexId not in ( select indexId from ProfileItem )");
|
await SQLiteHelper.Instance.ExecuteAsync($"delete from ProfileGroupItem where IndexId not in ( select indexId from ProfileItem )");
|
||||||
|
|
@ -42,347 +37,9 @@ public class ProfileGroupItemManager
|
||||||
_items = new ConcurrentDictionary<string, ProfileGroupItem>(list.Where(t => !string.IsNullOrEmpty(t.IndexId)).ToDictionary(t => t.IndexId!));
|
_items = new ConcurrentDictionary<string, ProfileGroupItem>(list.Where(t => !string.IsNullOrEmpty(t.IndexId)).ToDictionary(t => t.IndexId!));
|
||||||
}
|
}
|
||||||
|
|
||||||
private ProfileGroupItem AddProfileGroupItem(string indexId)
|
|
||||||
{
|
|
||||||
var profileGroupItem = new ProfileGroupItem()
|
|
||||||
{
|
|
||||||
IndexId = indexId,
|
|
||||||
ChildItems = string.Empty,
|
|
||||||
MultipleLoad = EMultipleLoad.LeastPing
|
|
||||||
};
|
|
||||||
|
|
||||||
_items[indexId] = profileGroupItem;
|
|
||||||
return profileGroupItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ProfileGroupItem GetProfileGroupItem(string indexId)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(indexId))
|
|
||||||
{
|
|
||||||
indexId = Utils.GetGuid(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _items.GetOrAdd(indexId, AddProfileGroupItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task ClearAll()
|
public async Task ClearAll()
|
||||||
{
|
{
|
||||||
await SQLiteHelper.Instance.ExecuteAsync($"delete from ProfileGroupItem ");
|
await SQLiteHelper.Instance.ExecuteAsync($"delete from ProfileGroupItem ");
|
||||||
_items.Clear();
|
_items.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SaveTo()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var lstExists = await SQLiteHelper.Instance.TableAsync<ProfileGroupItem>().ToListAsync();
|
|
||||||
var existsMap = lstExists.Where(t => !string.IsNullOrEmpty(t.IndexId)).ToDictionary(t => t.IndexId!);
|
|
||||||
|
|
||||||
var lstInserts = new List<ProfileGroupItem>();
|
|
||||||
var lstUpdates = new List<ProfileGroupItem>();
|
|
||||||
|
|
||||||
foreach (var item in _items.Values)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(item.IndexId))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (existsMap.ContainsKey(item.IndexId))
|
|
||||||
{
|
|
||||||
lstUpdates.Add(item);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lstInserts.Add(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (lstInserts.Count > 0)
|
|
||||||
{
|
|
||||||
await SQLiteHelper.Instance.InsertAllAsync(lstInserts);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lstUpdates.Count > 0)
|
|
||||||
{
|
|
||||||
await SQLiteHelper.Instance.UpdateAllAsync(lstUpdates);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.SaveLog(_tag, ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.SaveLog(_tag, ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ProfileGroupItem GetOrCreateAndMarkDirty(string indexId)
|
|
||||||
{
|
|
||||||
return GetProfileGroupItem(indexId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async ValueTask DisposeAsync()
|
|
||||||
{
|
|
||||||
await SaveTo();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task SaveItemAsync(ProfileGroupItem item)
|
|
||||||
{
|
|
||||||
if (item is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(item));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(item.IndexId))
|
|
||||||
{
|
|
||||||
throw new ArgumentException("IndexId required", nameof(item));
|
|
||||||
}
|
|
||||||
|
|
||||||
_items[item.IndexId] = item;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var lst = await SQLiteHelper.Instance.TableAsync<ProfileGroupItem>().Where(t => t.IndexId == item.IndexId).ToListAsync();
|
|
||||||
if (lst != null && lst.Count > 0)
|
|
||||||
{
|
|
||||||
await SQLiteHelper.Instance.UpdateAllAsync(new List<ProfileGroupItem> { item });
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await SQLiteHelper.Instance.InsertAllAsync(new List<ProfileGroupItem> { item });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.SaveLog(_tag, ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Helper
|
|
||||||
|
|
||||||
public static bool HasCycle(string? indexId)
|
|
||||||
{
|
|
||||||
return HasCycle(indexId, new HashSet<string>(), new HashSet<string>());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool HasCycle(string? indexId, HashSet<string> visited, HashSet<string> stack)
|
|
||||||
{
|
|
||||||
if (indexId.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stack.Contains(indexId))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (visited.Contains(indexId))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
visited.Add(indexId);
|
|
||||||
stack.Add(indexId);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Instance.TryGet(indexId, out var groupItem);
|
|
||||||
|
|
||||||
if (groupItem == null || groupItem.ChildItems.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var childIds = Utils.String2List(groupItem.ChildItems)
|
|
||||||
.Where(p => !string.IsNullOrEmpty(p))
|
|
||||||
.ToList();
|
|
||||||
if (childIds == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var child in childIds)
|
|
||||||
{
|
|
||||||
if (HasCycle(child, visited, stack))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
stack.Remove(indexId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<(List<ProfileItem> Items, ProfileGroupItem? Group)> GetChildProfileItems(string? indexId)
|
|
||||||
{
|
|
||||||
Instance.TryGet(indexId, out var profileGroupItem);
|
|
||||||
if (profileGroupItem == null || profileGroupItem.NotHasChild())
|
|
||||||
{
|
|
||||||
return (new List<ProfileItem>(), profileGroupItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
var items = new List<ProfileItem>();
|
|
||||||
items.AddRange(await GetSubChildProfileItems(profileGroupItem));
|
|
||||||
items.AddRange(await GetChildProfileItems(profileGroupItem));
|
|
||||||
|
|
||||||
return (items, profileGroupItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<List<ProfileItem>> GetChildProfileItems(ProfileGroupItem? group)
|
|
||||||
{
|
|
||||||
if (group == null || group.ChildItems.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
return new();
|
|
||||||
}
|
|
||||||
var childProfiles = (await Task.WhenAll(
|
|
||||||
Utils.String2List(group.ChildItems)
|
|
||||||
.Where(p => !p.IsNullOrEmpty())
|
|
||||||
.Select(AppManager.Instance.GetProfileItem)
|
|
||||||
))
|
|
||||||
.Where(p =>
|
|
||||||
p != null &&
|
|
||||||
p.IsValid() &&
|
|
||||||
p.ConfigType != EConfigType.Custom
|
|
||||||
)
|
|
||||||
.ToList();
|
|
||||||
return childProfiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<List<ProfileItem>> GetSubChildProfileItems(ProfileGroupItem? group)
|
|
||||||
{
|
|
||||||
if (group == null || group.SubChildItems.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
return new();
|
|
||||||
}
|
|
||||||
var childProfiles = await AppManager.Instance.ProfileItems(group.SubChildItems);
|
|
||||||
|
|
||||||
return childProfiles.Where(p =>
|
|
||||||
p != null &&
|
|
||||||
p.IsValid() &&
|
|
||||||
!p.ConfigType.IsComplexType() &&
|
|
||||||
(group.Filter.IsNullOrEmpty() || Regex.IsMatch(p.Remarks, group.Filter))
|
|
||||||
)
|
|
||||||
.ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<HashSet<string>> GetAllChildDomainAddresses(string indexId)
|
|
||||||
{
|
|
||||||
// include grand children
|
|
||||||
var childAddresses = new HashSet<string>();
|
|
||||||
if (!Instance.TryGet(indexId, out var groupItem) || groupItem == null)
|
|
||||||
{
|
|
||||||
return childAddresses;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (groupItem.SubChildItems.IsNotEmpty())
|
|
||||||
{
|
|
||||||
var subItems = await GetSubChildProfileItems(groupItem);
|
|
||||||
subItems.ForEach(p => childAddresses.Add(p.Address));
|
|
||||||
}
|
|
||||||
|
|
||||||
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.IsGroupType())
|
|
||||||
{
|
|
||||||
var subAddresses = await GetAllChildDomainAddresses(childNode.IndexId);
|
|
||||||
foreach (var addr in subAddresses)
|
|
||||||
{
|
|
||||||
childAddresses.Add(addr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return childAddresses;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<HashSet<string>> GetAllChildEchQuerySni(string indexId)
|
|
||||||
{
|
|
||||||
// include grand children
|
|
||||||
var childAddresses = new HashSet<string>();
|
|
||||||
if (!Instance.TryGet(indexId, out var groupItem) || groupItem == null)
|
|
||||||
{
|
|
||||||
return childAddresses;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (groupItem.SubChildItems.IsNotEmpty())
|
|
||||||
{
|
|
||||||
var subItems = await GetSubChildProfileItems(groupItem);
|
|
||||||
foreach (var childNode in subItems)
|
|
||||||
{
|
|
||||||
if (childNode.EchConfigList.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (childNode.StreamSecurity == Global.StreamSecurity
|
|
||||||
&& childNode.EchConfigList?.Contains("://") == true)
|
|
||||||
{
|
|
||||||
var idx = childNode.EchConfigList.IndexOf('+');
|
|
||||||
childAddresses.Add(idx > 0 ? childNode.EchConfigList[..idx] : childNode.Sni);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
childAddresses.Add(childNode.Sni);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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() && !childNode.EchConfigList.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
if (childNode.StreamSecurity == Global.StreamSecurity
|
|
||||||
&& childNode.EchConfigList?.Contains("://") == true)
|
|
||||||
{
|
|
||||||
var idx = childNode.EchConfigList.IndexOf('+');
|
|
||||||
childAddresses.Add(idx > 0 ? childNode.EchConfigList[..idx] : childNode.Sni);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
childAddresses.Add(childNode.Sni);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (childNode.ConfigType.IsGroupType())
|
|
||||||
{
|
|
||||||
var subAddresses = await GetAllChildDomainAddresses(childNode.IndexId);
|
|
||||||
foreach (var addr in subAddresses)
|
|
||||||
{
|
|
||||||
childAddresses.Add(addr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return childAddresses;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Helper
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
namespace ServiceLib.Models;
|
namespace ServiceLib.Models;
|
||||||
|
|
||||||
|
// deprecated
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class ProfileGroupItem
|
public class ProfileGroupItem
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ public class ProfileItem : ReactiveObject
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Global.Flows.Contains(Flow))
|
if (!Global.Flows.Contains(GetExtraItem().Flow ?? string.Empty))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -125,6 +125,20 @@ public class ProfileItem : ReactiveObject
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetExtraItem(ProtocolExtraItem extraItem)
|
||||||
|
{
|
||||||
|
JsonData = JsonUtils.Serialize(extraItem, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProtocolExtraItem GetExtraItem()
|
||||||
|
{
|
||||||
|
if (JsonData.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
return new ProtocolExtraItem();
|
||||||
|
}
|
||||||
|
return JsonUtils.Deserialize<ProtocolExtraItem>(JsonData);
|
||||||
|
}
|
||||||
|
|
||||||
#endregion function
|
#endregion function
|
||||||
|
|
||||||
[PrimaryKey]
|
[PrimaryKey]
|
||||||
|
|
@ -134,9 +148,7 @@ public class ProfileItem : ReactiveObject
|
||||||
public int ConfigVersion { get; set; }
|
public int ConfigVersion { get; set; }
|
||||||
public string Address { get; set; }
|
public string Address { get; set; }
|
||||||
public int Port { get; set; }
|
public int Port { get; set; }
|
||||||
public string Ports { get; set; }
|
|
||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
public int AlterId { get; set; }
|
|
||||||
public string Security { get; set; }
|
public string Security { get; set; }
|
||||||
public string Network { get; set; }
|
public string Network { get; set; }
|
||||||
public string Remarks { get; set; }
|
public string Remarks { get; set; }
|
||||||
|
|
@ -147,7 +159,6 @@ public class ProfileItem : ReactiveObject
|
||||||
public string AllowInsecure { get; set; }
|
public string AllowInsecure { get; set; }
|
||||||
public string Subid { get; set; }
|
public string Subid { get; set; }
|
||||||
public bool IsSub { get; set; } = true;
|
public bool IsSub { get; set; } = true;
|
||||||
public string Flow { get; set; }
|
|
||||||
public string Sni { get; set; }
|
public string Sni { get; set; }
|
||||||
public string Alpn { get; set; } = string.Empty;
|
public string Alpn { get; set; } = string.Empty;
|
||||||
public ECoreType? CoreType { get; set; }
|
public ECoreType? CoreType { get; set; }
|
||||||
|
|
@ -164,4 +175,9 @@ public class ProfileItem : ReactiveObject
|
||||||
public string CertSha { get; set; }
|
public string CertSha { get; set; }
|
||||||
public string EchConfigList { get; set; }
|
public string EchConfigList { get; set; }
|
||||||
public string EchForceQuery { get; set; }
|
public string EchForceQuery { get; set; }
|
||||||
|
public string JsonData { get; set; }
|
||||||
|
// deprecated
|
||||||
|
public string Ports { get; set; }
|
||||||
|
public int AlterId { get; set; }
|
||||||
|
public string Flow { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
27
v2rayN/ServiceLib/Models/ProtocolExtraItem.cs
Normal file
27
v2rayN/ServiceLib/Models/ProtocolExtraItem.cs
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
namespace ServiceLib.Models;
|
||||||
|
|
||||||
|
public class ProtocolExtraItem
|
||||||
|
{
|
||||||
|
// vmess
|
||||||
|
public string? AlterId { get; set; }
|
||||||
|
|
||||||
|
// vless
|
||||||
|
public string? Flow { get; set; }
|
||||||
|
//public string? VisionSeed { get; set; }
|
||||||
|
|
||||||
|
// shadowsocks
|
||||||
|
//public string? PluginArgs { get; set; }
|
||||||
|
|
||||||
|
// hysteria2
|
||||||
|
public string? UpMbps { get; set; }
|
||||||
|
public string? DownMbps { get; set; }
|
||||||
|
public string? Ports { get; set; }
|
||||||
|
public string? HopInterval { get; set; }
|
||||||
|
|
||||||
|
// group profile
|
||||||
|
public string? GroupType { get; set; }
|
||||||
|
public string? ChildItems { get; set; }
|
||||||
|
public string? SubChildItems { get; set; }
|
||||||
|
public string? Filter { get; set; }
|
||||||
|
public EMultipleLoad? MultipleLoad { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -204,7 +204,7 @@ public partial class CoreConfigSingboxService
|
||||||
}
|
}
|
||||||
else if (node?.ConfigType.IsGroupType() == true)
|
else if (node?.ConfigType.IsGroupType() == true)
|
||||||
{
|
{
|
||||||
var queryServerNames = (await ProfileGroupItemManager.GetAllChildEchQuerySni(node.IndexId)).ToList();
|
var queryServerNames = (await GroupProfileManager.GetAllChildEchQuerySni(node)).ToList();
|
||||||
if (queryServerNames.Count > 0)
|
if (queryServerNames.Count > 0)
|
||||||
{
|
{
|
||||||
singboxConfig.dns.rules.Add(new()
|
singboxConfig.dns.rules.Add(new()
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ public partial class CoreConfigSingboxService
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
var extraItem = node.GetExtraItem();
|
||||||
outbound.server = node.Address;
|
outbound.server = node.Address;
|
||||||
outbound.server_port = node.Port;
|
outbound.server_port = node.Port;
|
||||||
outbound.type = Global.ProtocolTypes[node.ConfigType];
|
outbound.type = Global.ProtocolTypes[node.ConfigType];
|
||||||
|
|
@ -15,7 +16,7 @@ public partial class CoreConfigSingboxService
|
||||||
case EConfigType.VMess:
|
case EConfigType.VMess:
|
||||||
{
|
{
|
||||||
outbound.uuid = node.Id;
|
outbound.uuid = node.Id;
|
||||||
outbound.alter_id = node.AlterId;
|
outbound.alter_id = int.TryParse(extraItem?.AlterId, out var result) ? result : 0;
|
||||||
if (Global.VmessSecurities.Contains(node.Security))
|
if (Global.VmessSecurities.Contains(node.Security))
|
||||||
{
|
{
|
||||||
outbound.security = node.Security;
|
outbound.security = node.Security;
|
||||||
|
|
@ -112,13 +113,13 @@ public partial class CoreConfigSingboxService
|
||||||
|
|
||||||
outbound.packet_encoding = "xudp";
|
outbound.packet_encoding = "xudp";
|
||||||
|
|
||||||
if (node.Flow.IsNullOrEmpty())
|
if (extraItem.Flow.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
await GenOutboundMux(node, outbound);
|
await GenOutboundMux(node, outbound);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
outbound.flow = node.Flow;
|
outbound.flow = extraItem.Flow;
|
||||||
}
|
}
|
||||||
|
|
||||||
await GenOutboundTransport(node, outbound);
|
await GenOutboundTransport(node, outbound);
|
||||||
|
|
@ -145,12 +146,14 @@ public partial class CoreConfigSingboxService
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
outbound.up_mbps = _config.HysteriaItem.UpMbps > 0 ? _config.HysteriaItem.UpMbps : null;
|
var extra = node.GetExtraItem();
|
||||||
outbound.down_mbps = _config.HysteriaItem.DownMbps > 0 ? _config.HysteriaItem.DownMbps : null;
|
outbound.up_mbps = int.TryParse(extra?.UpMbps, out var upMbps) && upMbps >= 0 ? upMbps : (_config.HysteriaItem.UpMbps > 0 ? _config.HysteriaItem.UpMbps : null);
|
||||||
if (node.Ports.IsNotEmpty() && (node.Ports.Contains(':') || node.Ports.Contains('-') || node.Ports.Contains(',')))
|
outbound.down_mbps = int.TryParse(extra?.DownMbps, out var downMbps) && downMbps >= 0 ? downMbps : (_config.HysteriaItem.DownMbps > 0 ? _config.HysteriaItem.DownMbps : null);
|
||||||
|
var ports = extra?.Ports?.IsNullOrEmpty() == false ? extra.Ports : null;
|
||||||
|
if ((!ports.IsNullOrEmpty()) && (ports.Contains(':') || ports.Contains('-') || ports.Contains(',')))
|
||||||
{
|
{
|
||||||
outbound.server_port = null;
|
outbound.server_port = null;
|
||||||
outbound.server_ports = node.Ports.Split(',')
|
outbound.server_ports = ports.Split(',')
|
||||||
.Select(p => p.Trim())
|
.Select(p => p.Trim())
|
||||||
.Where(p => p.IsNotEmpty())
|
.Where(p => p.IsNotEmpty())
|
||||||
.Select(p =>
|
.Select(p =>
|
||||||
|
|
@ -453,13 +456,13 @@ public partial class CoreConfigSingboxService
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
var hasCycle = ProfileGroupItemManager.HasCycle(node.IndexId);
|
var hasCycle = await GroupProfileManager.HasCycle(node);
|
||||||
if (hasCycle)
|
if (hasCycle)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
var (childProfiles, profileGroupItem) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId);
|
var (childProfiles, profileExtraItem) = await GroupProfileManager.GetChildProfileItems(node);
|
||||||
if (childProfiles.Count <= 0)
|
if (childProfiles.Count <= 0)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -467,13 +470,14 @@ public partial class CoreConfigSingboxService
|
||||||
switch (node.ConfigType)
|
switch (node.ConfigType)
|
||||||
{
|
{
|
||||||
case EConfigType.PolicyGroup:
|
case EConfigType.PolicyGroup:
|
||||||
|
var multipleLoad = profileExtraItem?.MultipleLoad ?? EMultipleLoad.LeastPing;
|
||||||
if (ignoreOriginChain)
|
if (ignoreOriginChain)
|
||||||
{
|
{
|
||||||
await GenOutboundsList(childProfiles, singboxConfig, profileGroupItem.MultipleLoad, baseTagName);
|
await GenOutboundsList(childProfiles, singboxConfig, multipleLoad, baseTagName);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await GenOutboundsListWithChain(childProfiles, singboxConfig, profileGroupItem.MultipleLoad, baseTagName);
|
await GenOutboundsListWithChain(childProfiles, singboxConfig, multipleLoad, baseTagName);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
@ -585,7 +589,7 @@ public partial class CoreConfigSingboxService
|
||||||
|
|
||||||
if (node.ConfigType.IsGroupType())
|
if (node.ConfigType.IsGroupType())
|
||||||
{
|
{
|
||||||
var (childProfiles, profileGroupItem) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId);
|
var (childProfiles, profileExtraItem) = await GroupProfileManager.GetChildProfileItems(node);
|
||||||
if (childProfiles.Count <= 0)
|
if (childProfiles.Count <= 0)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -594,7 +598,8 @@ public partial class CoreConfigSingboxService
|
||||||
var ret = node.ConfigType switch
|
var ret = node.ConfigType switch
|
||||||
{
|
{
|
||||||
EConfigType.PolicyGroup =>
|
EConfigType.PolicyGroup =>
|
||||||
await GenOutboundsListWithChain(childProfiles, singboxConfig, profileGroupItem.MultipleLoad, childBaseTagName),
|
await GenOutboundsListWithChain(childProfiles, singboxConfig,
|
||||||
|
profileExtraItem?.MultipleLoad ?? EMultipleLoad.LeastPing, childBaseTagName),
|
||||||
EConfigType.ProxyChain =>
|
EConfigType.ProxyChain =>
|
||||||
await GenChainOutboundsList(childProfiles, singboxConfig, childBaseTagName),
|
await GenChainOutboundsList(childProfiles, singboxConfig, childBaseTagName),
|
||||||
_ => throw new NotImplementedException()
|
_ => throw new NotImplementedException()
|
||||||
|
|
@ -763,7 +768,7 @@ public partial class CoreConfigSingboxService
|
||||||
|
|
||||||
if (node.ConfigType.IsGroupType())
|
if (node.ConfigType.IsGroupType())
|
||||||
{
|
{
|
||||||
var (childProfiles, profileGroupItem) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId);
|
var (childProfiles, profileExtraItem) = await GroupProfileManager.GetChildProfileItems(node);
|
||||||
if (childProfiles.Count <= 0)
|
if (childProfiles.Count <= 0)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -772,7 +777,7 @@ public partial class CoreConfigSingboxService
|
||||||
var ret = node.ConfigType switch
|
var ret = node.ConfigType switch
|
||||||
{
|
{
|
||||||
EConfigType.PolicyGroup =>
|
EConfigType.PolicyGroup =>
|
||||||
await GenOutboundsList(childProfiles, singboxConfig, profileGroupItem.MultipleLoad, childBaseTagName),
|
await GenOutboundsList(childProfiles, singboxConfig, profileExtraItem?.MultipleLoad ?? EMultipleLoad.LeastPing, childBaseTagName),
|
||||||
EConfigType.ProxyChain =>
|
EConfigType.ProxyChain =>
|
||||||
await GenChainOutboundsList(childProfiles, singboxConfig, childBaseTagName),
|
await GenChainOutboundsList(childProfiles, singboxConfig, childBaseTagName),
|
||||||
_ => throw new NotImplementedException()
|
_ => throw new NotImplementedException()
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
var extraItem = node.GetExtraItem();
|
||||||
var muxEnabled = node.MuxEnabled ?? _config.CoreBasicItem.MuxEnabled;
|
var muxEnabled = node.MuxEnabled ?? _config.CoreBasicItem.MuxEnabled;
|
||||||
switch (node.ConfigType)
|
switch (node.ConfigType)
|
||||||
{
|
{
|
||||||
|
|
@ -36,7 +37,7 @@ public partial class CoreConfigV2rayService
|
||||||
}
|
}
|
||||||
|
|
||||||
usersItem.id = node.Id;
|
usersItem.id = node.Id;
|
||||||
usersItem.alterId = node.AlterId;
|
usersItem.alterId = int.TryParse(extraItem?.AlterId, out var result) ? result : 0;
|
||||||
usersItem.email = Global.UserEMail;
|
usersItem.email = Global.UserEMail;
|
||||||
if (Global.VmessSecurities.Contains(node.Security))
|
if (Global.VmessSecurities.Contains(node.Security))
|
||||||
{
|
{
|
||||||
|
|
@ -142,13 +143,13 @@ public partial class CoreConfigV2rayService
|
||||||
usersItem.email = Global.UserEMail;
|
usersItem.email = Global.UserEMail;
|
||||||
usersItem.encryption = node.Security;
|
usersItem.encryption = node.Security;
|
||||||
|
|
||||||
if (node.Flow.IsNullOrEmpty())
|
if (extraItem.Flow.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
await GenOutboundMux(node, outbound, muxEnabled, muxEnabled);
|
await GenOutboundMux(node, outbound, muxEnabled, muxEnabled);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
usersItem.flow = node.Flow;
|
usersItem.flow = extraItem.Flow;
|
||||||
await GenOutboundMux(node, outbound, false, muxEnabled);
|
await GenOutboundMux(node, outbound, false, muxEnabled);
|
||||||
}
|
}
|
||||||
outbound.settings.servers = null;
|
outbound.settings.servers = null;
|
||||||
|
|
@ -509,13 +510,15 @@ public partial class CoreConfigV2rayService
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "hysteria":
|
case "hysteria":
|
||||||
|
var extraItem = node.GetExtraItem();
|
||||||
|
var ports = extraItem?.Ports;
|
||||||
HysteriaUdpHop4Ray? udpHop = null;
|
HysteriaUdpHop4Ray? udpHop = null;
|
||||||
if (node.Ports.IsNotEmpty() &&
|
if (!ports.IsNullOrEmpty() &&
|
||||||
(node.Ports.Contains(':') || node.Ports.Contains('-') || node.Ports.Contains(',')))
|
(ports.Contains(':') || ports.Contains('-') || ports.Contains(',')))
|
||||||
{
|
{
|
||||||
udpHop = new()
|
udpHop = new()
|
||||||
{
|
{
|
||||||
ports = node.Ports.Replace(':', '-'),
|
ports = ports.Replace(':', '-'),
|
||||||
interval = _config.HysteriaItem.HopInterval > 0
|
interval = _config.HysteriaItem.HopInterval > 0
|
||||||
? _config.HysteriaItem.HopInterval
|
? _config.HysteriaItem.HopInterval
|
||||||
: null,
|
: null,
|
||||||
|
|
@ -592,13 +595,13 @@ public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
var hasCycle = ProfileGroupItemManager.HasCycle(node.IndexId);
|
var hasCycle = await GroupProfileManager.HasCycle(node);
|
||||||
if (hasCycle)
|
if (hasCycle)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
var (childProfiles, profileGroupItem) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId);
|
var (childProfiles, profileExtraItem) = await GroupProfileManager.GetChildProfileItems(node);
|
||||||
if (childProfiles.Count <= 0)
|
if (childProfiles.Count <= 0)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -627,8 +630,9 @@ public partial class CoreConfigV2rayService
|
||||||
//add balancers
|
//add balancers
|
||||||
if (node.ConfigType == EConfigType.PolicyGroup)
|
if (node.ConfigType == EConfigType.PolicyGroup)
|
||||||
{
|
{
|
||||||
await GenObservatory(v2rayConfig, profileGroupItem.MultipleLoad, baseTagName);
|
var multipleLoad = profileExtraItem?.MultipleLoad ?? EMultipleLoad.LeastPing;
|
||||||
await GenBalancer(v2rayConfig, profileGroupItem.MultipleLoad, baseTagName);
|
await GenObservatory(v2rayConfig, multipleLoad, baseTagName);
|
||||||
|
await GenBalancer(v2rayConfig, multipleLoad, baseTagName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
@ -737,7 +741,7 @@ public partial class CoreConfigV2rayService
|
||||||
|
|
||||||
if (node.ConfigType.IsGroupType())
|
if (node.ConfigType.IsGroupType())
|
||||||
{
|
{
|
||||||
var (childProfiles, _) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId);
|
var (childProfiles, _) = await GroupProfileManager.GetChildProfileItems(node);
|
||||||
if (childProfiles.Count <= 0)
|
if (childProfiles.Count <= 0)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -891,7 +895,7 @@ public partial class CoreConfigV2rayService
|
||||||
|
|
||||||
if (node.ConfigType.IsGroupType())
|
if (node.ConfigType.IsGroupType())
|
||||||
{
|
{
|
||||||
var (childProfiles, _) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId);
|
var (childProfiles, _) = await GroupProfileManager.GetChildProfileItems(node);
|
||||||
if (childProfiles.Count <= 0)
|
if (childProfiles.Count <= 0)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
|
|
|
||||||
|
|
@ -79,8 +79,8 @@ public class AddGroupServerViewModel : MyReactiveObject
|
||||||
|
|
||||||
public async Task Init()
|
public async Task Init()
|
||||||
{
|
{
|
||||||
ProfileGroupItemManager.Instance.TryGet(SelectedSource.IndexId, out var profileGroup);
|
var extra = SelectedSource.GetExtraItem();
|
||||||
PolicyGroupType = (profileGroup?.MultipleLoad ?? EMultipleLoad.LeastPing) switch
|
PolicyGroupType = (extra?.MultipleLoad ?? EMultipleLoad.LeastPing) switch
|
||||||
{
|
{
|
||||||
EMultipleLoad.LeastPing => ResUI.TbLeastPing,
|
EMultipleLoad.LeastPing => ResUI.TbLeastPing,
|
||||||
EMultipleLoad.Fallback => ResUI.TbFallback,
|
EMultipleLoad.Fallback => ResUI.TbFallback,
|
||||||
|
|
@ -93,22 +93,18 @@ public class AddGroupServerViewModel : MyReactiveObject
|
||||||
var subs = await AppManager.Instance.SubItems();
|
var subs = await AppManager.Instance.SubItems();
|
||||||
subs.Add(new SubItem());
|
subs.Add(new SubItem());
|
||||||
SubItems.AddRange(subs);
|
SubItems.AddRange(subs);
|
||||||
SelectedSubItem = SubItems.Where(s => s.Id == profileGroup?.SubChildItems).FirstOrDefault();
|
SelectedSubItem = SubItems.FirstOrDefault(s => s.Id == extra?.SubChildItems);
|
||||||
Filter = profileGroup?.Filter;
|
Filter = extra?.Filter;
|
||||||
|
|
||||||
var childItemMulti = ProfileGroupItemManager.Instance.GetOrCreateAndMarkDirty(SelectedSource?.IndexId);
|
var childIndexIds = Utils.String2List(extra?.ChildItems) ?? [];
|
||||||
if (childItemMulti != null)
|
foreach (var item in childIndexIds)
|
||||||
{
|
{
|
||||||
var childIndexIds = Utils.String2List(childItemMulti.ChildItems) ?? [];
|
var child = await AppManager.Instance.GetProfileItem(item);
|
||||||
foreach (var item in childIndexIds)
|
if (child == null)
|
||||||
{
|
{
|
||||||
var child = await AppManager.Instance.GetProfileItem(item);
|
continue;
|
||||||
if (child == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
ChildItemsObs.Add(child);
|
|
||||||
}
|
}
|
||||||
|
ChildItemsObs.Add(child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -205,18 +201,11 @@ public class AddGroupServerViewModel : MyReactiveObject
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var childIndexIds = new List<string>();
|
|
||||||
foreach (var item in ChildItemsObs)
|
var extra = SelectedSource.GetExtraItem();
|
||||||
{
|
extra.ChildItems =
|
||||||
if (item.IndexId.IsNullOrEmpty())
|
Utils.List2String(ChildItemsObs.Where(s => !s.IndexId.IsNullOrEmpty()).Select(s => s.IndexId).ToList());
|
||||||
{
|
extra.MultipleLoad = PolicyGroupType switch
|
||||||
continue;
|
|
||||||
}
|
|
||||||
childIndexIds.Add(item.IndexId);
|
|
||||||
}
|
|
||||||
var profileGroup = ProfileGroupItemManager.Instance.GetOrCreateAndMarkDirty(SelectedSource.IndexId);
|
|
||||||
profileGroup.ChildItems = Utils.List2String(childIndexIds);
|
|
||||||
profileGroup.MultipleLoad = PolicyGroupType switch
|
|
||||||
{
|
{
|
||||||
var s when s == ResUI.TbLeastPing => EMultipleLoad.LeastPing,
|
var s when s == ResUI.TbLeastPing => EMultipleLoad.LeastPing,
|
||||||
var s when s == ResUI.TbFallback => EMultipleLoad.Fallback,
|
var s when s == ResUI.TbFallback => EMultipleLoad.Fallback,
|
||||||
|
|
@ -226,17 +215,19 @@ public class AddGroupServerViewModel : MyReactiveObject
|
||||||
_ => EMultipleLoad.LeastPing,
|
_ => EMultipleLoad.LeastPing,
|
||||||
};
|
};
|
||||||
|
|
||||||
profileGroup.SubChildItems = SelectedSubItem?.Id;
|
extra.SubChildItems = SelectedSubItem?.Id;
|
||||||
profileGroup.Filter = Filter;
|
extra.Filter = Filter;
|
||||||
|
|
||||||
var hasCycle = ProfileGroupItemManager.HasCycle(profileGroup.IndexId);
|
var hasCycle = await GroupProfileManager.HasCycle(SelectedSource.IndexId, extra);
|
||||||
if (hasCycle)
|
if (hasCycle)
|
||||||
{
|
{
|
||||||
NoticeManager.Instance.Enqueue(string.Format(ResUI.GroupSelfReference, remarks));
|
NoticeManager.Instance.Enqueue(string.Format(ResUI.GroupSelfReference, remarks));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (await ConfigHandler.AddGroupServerCommon(_config, SelectedSource, profileGroup, true) == 0)
|
SelectedSource.SetExtraItem(extra);
|
||||||
|
|
||||||
|
if (await ConfigHandler.AddServerCommon(_config, SelectedSource) == 0)
|
||||||
{
|
{
|
||||||
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
|
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
|
||||||
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,15 @@ public class AddServerViewModel : MyReactiveObject
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public string CertSha { get; set; }
|
public string CertSha { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public int AlterId { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public string Ports { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public string Flow { get; set; }
|
||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> FetchCertCmd { get; }
|
public ReactiveCommand<Unit, Unit> FetchCertCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> FetchCertChainCmd { get; }
|
public ReactiveCommand<Unit, Unit> FetchCertChainCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
||||||
|
|
@ -63,6 +72,11 @@ public class AddServerViewModel : MyReactiveObject
|
||||||
CoreType = SelectedSource?.CoreType?.ToString();
|
CoreType = SelectedSource?.CoreType?.ToString();
|
||||||
Cert = SelectedSource?.Cert?.ToString() ?? string.Empty;
|
Cert = SelectedSource?.Cert?.ToString() ?? string.Empty;
|
||||||
CertSha = SelectedSource?.CertSha?.ToString() ?? string.Empty;
|
CertSha = SelectedSource?.CertSha?.ToString() ?? string.Empty;
|
||||||
|
|
||||||
|
var extraItem = SelectedSource?.GetExtraItem();
|
||||||
|
Ports = extraItem?.Ports ?? string.Empty;
|
||||||
|
AlterId = int.TryParse(extraItem?.AlterId, out var result) ? result : 0;
|
||||||
|
Flow = extraItem?.Flow ?? string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SaveServerAsync()
|
private async Task SaveServerAsync()
|
||||||
|
|
@ -109,6 +123,11 @@ public class AddServerViewModel : MyReactiveObject
|
||||||
SelectedSource.CoreType = CoreType.IsNullOrEmpty() ? null : (ECoreType)Enum.Parse(typeof(ECoreType), CoreType);
|
SelectedSource.CoreType = CoreType.IsNullOrEmpty() ? null : (ECoreType)Enum.Parse(typeof(ECoreType), CoreType);
|
||||||
SelectedSource.Cert = Cert.IsNullOrEmpty() ? string.Empty : Cert;
|
SelectedSource.Cert = Cert.IsNullOrEmpty() ? string.Empty : Cert;
|
||||||
SelectedSource.CertSha = CertSha.IsNullOrEmpty() ? string.Empty : CertSha;
|
SelectedSource.CertSha = CertSha.IsNullOrEmpty() ? string.Empty : CertSha;
|
||||||
|
var extraItem = SelectedSource.GetExtraItem();
|
||||||
|
extraItem.Ports = Ports;
|
||||||
|
extraItem.AlterId = AlterId > 0 ? AlterId.ToString() : string.Empty;
|
||||||
|
extraItem.Flow = Flow;
|
||||||
|
SelectedSource.SetExtraItem(extraItem);
|
||||||
|
|
||||||
if (await ConfigHandler.AddServer(_config, SelectedSource) == 0)
|
if (await ConfigHandler.AddServer(_config, SelectedSource) == 0)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,7 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
||||||
{
|
{
|
||||||
case EConfigType.VMess:
|
case EConfigType.VMess:
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.AlterId, v => v.txtAlterId.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.AlterId, v => v.txtAlterId.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.cmbSecurity.SelectedValue).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.cmbSecurity.SelectedValue).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled.IsChecked).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled.IsChecked).DisposeWith(disposables);
|
||||||
break;
|
break;
|
||||||
|
|
@ -139,21 +139,21 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
||||||
|
|
||||||
case EConfigType.VLESS:
|
case EConfigType.VLESS:
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId5.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId5.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Flow, v => v.cmbFlow5.SelectedValue).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.Flow, v => v.cmbFlow5.SelectedValue).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.txtSecurity5.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.txtSecurity5.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled5.IsChecked).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled5.IsChecked).DisposeWith(disposables);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.Trojan:
|
case EConfigType.Trojan:
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId6.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId6.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Flow, v => v.cmbFlow6.SelectedValue).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.Flow, v => v.cmbFlow6.SelectedValue).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled6.IsChecked).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled6.IsChecked).DisposeWith(disposables);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.Hysteria2:
|
case EConfigType.Hysteria2:
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId7.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId7.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Path, v => v.txtPath7.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Path, v => v.txtPath7.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Ports, v => v.txtPorts7.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.Ports, v => v.txtPorts7.Text).DisposeWith(disposables);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.TUIC:
|
case EConfigType.TUIC:
|
||||||
|
|
|
||||||
|
|
@ -115,7 +115,7 @@ public partial class AddServerWindow
|
||||||
{
|
{
|
||||||
case EConfigType.VMess:
|
case EConfigType.VMess:
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.AlterId, v => v.txtAlterId.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.AlterId, v => v.txtAlterId.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.cmbSecurity.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.cmbSecurity.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled.IsChecked).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled.IsChecked).DisposeWith(disposables);
|
||||||
break;
|
break;
|
||||||
|
|
@ -134,21 +134,21 @@ public partial class AddServerWindow
|
||||||
|
|
||||||
case EConfigType.VLESS:
|
case EConfigType.VLESS:
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId5.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId5.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Flow, v => v.cmbFlow5.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.Flow, v => v.cmbFlow5.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.txtSecurity5.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.txtSecurity5.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled5.IsChecked).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled5.IsChecked).DisposeWith(disposables);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.Trojan:
|
case EConfigType.Trojan:
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId6.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId6.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Flow, v => v.cmbFlow6.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.Flow, v => v.cmbFlow6.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled6.IsChecked).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled6.IsChecked).DisposeWith(disposables);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.Hysteria2:
|
case EConfigType.Hysteria2:
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId7.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId7.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Path, v => v.txtPath7.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Path, v => v.txtPath7.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Ports, v => v.txtPorts7.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.Ports, v => v.txtPorts7.Text).DisposeWith(disposables);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.TUIC:
|
case EConfigType.TUIC:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue