mirror of
https://github.com/2dust/v2rayN.git
synced 2026-02-28 05:03:02 +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.EchConfigList = profileItem.EchConfigList;
|
||||
item.EchForceQuery = profileItem.EchForceQuery;
|
||||
item.JsonData = profileItem.JsonData;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
await AddServerCommon(config, profileItem, true);
|
||||
|
|
@ -967,9 +963,11 @@ public static class ConfigHandler
|
|||
profileItem.Path = profileItem.Path.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())
|
||||
{
|
||||
|
|
@ -980,6 +978,8 @@ public static class ConfigHandler
|
|||
profileItem.Security = Global.None;
|
||||
}
|
||||
|
||||
profileItem.SetExtraItem(extraItem);
|
||||
|
||||
await AddServerCommon(config, profileItem, toFile);
|
||||
|
||||
return 0;
|
||||
|
|
@ -1082,37 +1082,6 @@ public static class ConfigHandler
|
|||
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>
|
||||
/// Compare two profile items to determine if they represent the same server
|
||||
/// Used for deduplication and server matching
|
||||
|
|
@ -1128,6 +1097,8 @@ public static class ConfigHandler
|
|||
return false;
|
||||
}
|
||||
|
||||
var extraItem = o.GetExtraItem();
|
||||
|
||||
return o.ConfigType == n.ConfigType
|
||||
&& AreEqual(o.Address, n.Address)
|
||||
&& o.Port == n.Port
|
||||
|
|
@ -1138,7 +1109,7 @@ public static class ConfigHandler
|
|||
&& AreEqual(o.RequestHost, n.RequestHost)
|
||||
&& AreEqual(o.Path, n.Path)
|
||||
&& (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.Alpn, n.Alpn)
|
||||
&& AreEqual(o.Fingerprint, n.Fingerprint)
|
||||
|
|
@ -1199,7 +1170,7 @@ public static class ConfigHandler
|
|||
var indexId = Utils.GetGuid(false);
|
||||
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)
|
||||
{
|
||||
remark += multipleLoad switch
|
||||
|
|
@ -1233,13 +1204,12 @@ public static class ConfigHandler
|
|||
{
|
||||
profile.Subid = subId;
|
||||
}
|
||||
var profileGroup = new ProfileGroupItem
|
||||
var extraItem = new ProtocolExtraItem
|
||||
{
|
||||
ChildItems = childProfileIndexId,
|
||||
MultipleLoad = multipleLoad,
|
||||
IndexId = indexId,
|
||||
ChildItems = childProfileIndexId, MultipleLoad = multipleLoad,
|
||||
};
|
||||
var ret = await AddGroupServerCommon(config, profile, profileGroup, true);
|
||||
profile.SetExtraItem(extraItem);
|
||||
var ret = await AddServerCommon(config, profile, true);
|
||||
result.Success = ret == 0;
|
||||
result.Data = indexId;
|
||||
return result;
|
||||
|
|
@ -1261,7 +1231,7 @@ public static class ConfigHandler
|
|||
var tun2SocksAddress = node.Address;
|
||||
if (node.ConfigType.IsGroupType())
|
||||
{
|
||||
var lstAddresses = (await ProfileGroupItemManager.GetAllChildDomainAddresses(node.IndexId)).ToList();
|
||||
var lstAddresses = (await GroupProfileManager.GetAllChildDomainAddresses(node)).ToList();
|
||||
if (lstAddresses.Count > 0)
|
||||
{
|
||||
tun2SocksAddress = Utils.List2String(lstAddresses);
|
||||
|
|
|
|||
|
|
@ -21,9 +21,10 @@ public class BaseFmt
|
|||
|
||||
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())
|
||||
|
|
@ -208,7 +209,9 @@ public class BaseFmt
|
|||
|
||||
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.Sni = GetQueryValue(query, "sni");
|
||||
item.Alpn = GetQueryDecoded(query, "alpn");
|
||||
|
|
|
|||
|
|
@ -24,11 +24,15 @@ public class Hysteria2Fmt : BaseFmt
|
|||
var query = Utils.ParseQueryString(url.Query);
|
||||
ResolveUriQuery(query, ref item);
|
||||
item.Path = GetQueryDecoded(query, "obfs-password");
|
||||
item.Ports = GetQueryDecoded(query, "mport");
|
||||
if (item.CertSha.IsNullOrEmpty())
|
||||
{
|
||||
item.CertSha = GetQueryDecoded(query, "pinSHA256");
|
||||
}
|
||||
ProtocolExtraItem extraItem = new()
|
||||
{
|
||||
Ports = GetQueryDecoded(query, "mport")
|
||||
};
|
||||
item.SetExtraItem(extraItem);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
|
@ -55,9 +59,10 @@ public class Hysteria2Fmt : BaseFmt
|
|||
dicQuery.Add("obfs", "salamander");
|
||||
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())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ public class VmessFmt : BaseFmt
|
|||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var extraItem = item?.GetExtraItem();
|
||||
var vmessQRCode = new VmessQRCode
|
||||
{
|
||||
v = item.ConfigVersion,
|
||||
|
|
@ -30,7 +32,7 @@ public class VmessFmt : BaseFmt
|
|||
add = item.Address,
|
||||
port = item.Port,
|
||||
id = item.Id,
|
||||
aid = item.AlterId,
|
||||
aid = int.TryParse(extraItem?.AlterId, out var result) ? result : 0,
|
||||
scy = item.Security,
|
||||
net = item.Network,
|
||||
type = item.HeaderType,
|
||||
|
|
@ -76,7 +78,10 @@ public class VmessFmt : BaseFmt
|
|||
item.Address = Utils.ToString(vmessQRCode.add);
|
||||
item.Port = vmessQRCode.port;
|
||||
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 = vmessQRCode.scy.IsNotEmpty() ? vmessQRCode.scy : Global.DefaultSecurity;
|
||||
|
|
|
|||
|
|
@ -128,6 +128,8 @@ public class ActionPrecheckManager
|
|||
}
|
||||
}
|
||||
|
||||
var extraItem = item.GetExtraItem();
|
||||
|
||||
switch (item.ConfigType)
|
||||
{
|
||||
case EConfigType.VMess:
|
||||
|
|
@ -144,7 +146,7 @@ public class ActionPrecheckManager
|
|||
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"));
|
||||
}
|
||||
|
|
@ -202,37 +204,22 @@ public class ActionPrecheckManager
|
|||
{
|
||||
var errors = new List<string>();
|
||||
|
||||
ProfileGroupItemManager.Instance.TryGet(item.IndexId, out var group);
|
||||
if (group is null || group.NotHasChild())
|
||||
{
|
||||
errors.Add(string.Format(ResUI.GroupEmpty, item.Remarks));
|
||||
return errors;
|
||||
}
|
||||
|
||||
var hasCycle = ProfileGroupItemManager.HasCycle(item.IndexId);
|
||||
var hasCycle = await GroupProfileManager.HasCycle(item.IndexId, item.GetExtraItem());
|
||||
if (hasCycle)
|
||||
{
|
||||
errors.Add(string.Format(ResUI.GroupSelfReference, item.Remarks));
|
||||
return errors;
|
||||
}
|
||||
|
||||
var childIds = new List<string>();
|
||||
var subItems = await ProfileGroupItemManager.GetSubChildProfileItems(group);
|
||||
childIds.AddRangeSafe(subItems.Select(p => p.IndexId));
|
||||
childIds.AddRangeSafe(Utils.String2List(group.ChildItems));
|
||||
var (childItems, _) = await GroupProfileManager.GetChildProfileItems(item);
|
||||
|
||||
foreach (var child in childIds)
|
||||
foreach (var childItem in childItems)
|
||||
{
|
||||
var childErrors = new List<string>();
|
||||
if (child.IsNullOrEmpty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var childItem = await AppManager.Instance.GetProfileItem(child);
|
||||
if (childItem is null)
|
||||
{
|
||||
childErrors.Add(string.Format(ResUI.NodeTagNotExist, child));
|
||||
childErrors.Add(string.Format(ResUI.NodeTagNotExist, ""));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -94,6 +94,8 @@ public sealed class AppManager
|
|||
_ = StatePort;
|
||||
_ = StatePort2;
|
||||
|
||||
_ = MigrateProfileExtra();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -225,15 +227,6 @@ public sealed class AppManager
|
|||
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()
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
public ProfileGroupItem? GetOrDefault(string indexId)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(indexId) ? null : (_items.TryGetValue(indexId, out var v) ? v : null);
|
||||
}
|
||||
|
||||
private async Task InitData()
|
||||
{
|
||||
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!));
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
await SQLiteHelper.Instance.ExecuteAsync($"delete from ProfileGroupItem ");
|
||||
_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;
|
||||
|
||||
// deprecated
|
||||
[Serializable]
|
||||
public class ProfileGroupItem
|
||||
{
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ public class ProfileItem : ReactiveObject
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!Global.Flows.Contains(Flow))
|
||||
if (!Global.Flows.Contains(GetExtraItem().Flow ?? string.Empty))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -125,6 +125,20 @@ public class ProfileItem : ReactiveObject
|
|||
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
|
||||
|
||||
[PrimaryKey]
|
||||
|
|
@ -134,9 +148,7 @@ public class ProfileItem : ReactiveObject
|
|||
public int ConfigVersion { get; set; }
|
||||
public string Address { get; set; }
|
||||
public int Port { get; set; }
|
||||
public string Ports { get; set; }
|
||||
public string Id { get; set; }
|
||||
public int AlterId { get; set; }
|
||||
public string Security { get; set; }
|
||||
public string Network { get; set; }
|
||||
public string Remarks { get; set; }
|
||||
|
|
@ -147,7 +159,6 @@ public class ProfileItem : ReactiveObject
|
|||
public string AllowInsecure { get; set; }
|
||||
public string Subid { get; set; }
|
||||
public bool IsSub { get; set; } = true;
|
||||
public string Flow { get; set; }
|
||||
public string Sni { get; set; }
|
||||
public string Alpn { get; set; } = string.Empty;
|
||||
public ECoreType? CoreType { get; set; }
|
||||
|
|
@ -164,4 +175,9 @@ public class ProfileItem : ReactiveObject
|
|||
public string CertSha { get; set; }
|
||||
public string EchConfigList { 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)
|
||||
{
|
||||
var queryServerNames = (await ProfileGroupItemManager.GetAllChildEchQuerySni(node.IndexId)).ToList();
|
||||
var queryServerNames = (await GroupProfileManager.GetAllChildEchQuerySni(node)).ToList();
|
||||
if (queryServerNames.Count > 0)
|
||||
{
|
||||
singboxConfig.dns.rules.Add(new()
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ public partial class CoreConfigSingboxService
|
|||
{
|
||||
try
|
||||
{
|
||||
var extraItem = node.GetExtraItem();
|
||||
outbound.server = node.Address;
|
||||
outbound.server_port = node.Port;
|
||||
outbound.type = Global.ProtocolTypes[node.ConfigType];
|
||||
|
|
@ -15,7 +16,7 @@ public partial class CoreConfigSingboxService
|
|||
case EConfigType.VMess:
|
||||
{
|
||||
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))
|
||||
{
|
||||
outbound.security = node.Security;
|
||||
|
|
@ -112,13 +113,13 @@ public partial class CoreConfigSingboxService
|
|||
|
||||
outbound.packet_encoding = "xudp";
|
||||
|
||||
if (node.Flow.IsNullOrEmpty())
|
||||
if (extraItem.Flow.IsNullOrEmpty())
|
||||
{
|
||||
await GenOutboundMux(node, outbound);
|
||||
}
|
||||
else
|
||||
{
|
||||
outbound.flow = node.Flow;
|
||||
outbound.flow = extraItem.Flow;
|
||||
}
|
||||
|
||||
await GenOutboundTransport(node, outbound);
|
||||
|
|
@ -145,12 +146,14 @@ public partial class CoreConfigSingboxService
|
|||
};
|
||||
}
|
||||
|
||||
outbound.up_mbps = _config.HysteriaItem.UpMbps > 0 ? _config.HysteriaItem.UpMbps : null;
|
||||
outbound.down_mbps = _config.HysteriaItem.DownMbps > 0 ? _config.HysteriaItem.DownMbps : null;
|
||||
if (node.Ports.IsNotEmpty() && (node.Ports.Contains(':') || node.Ports.Contains('-') || node.Ports.Contains(',')))
|
||||
var extra = node.GetExtraItem();
|
||||
outbound.up_mbps = int.TryParse(extra?.UpMbps, out var upMbps) && upMbps >= 0 ? upMbps : (_config.HysteriaItem.UpMbps > 0 ? _config.HysteriaItem.UpMbps : null);
|
||||
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_ports = node.Ports.Split(',')
|
||||
outbound.server_ports = ports.Split(',')
|
||||
.Select(p => p.Trim())
|
||||
.Where(p => p.IsNotEmpty())
|
||||
.Select(p =>
|
||||
|
|
@ -453,13 +456,13 @@ public partial class CoreConfigSingboxService
|
|||
{
|
||||
return -1;
|
||||
}
|
||||
var hasCycle = ProfileGroupItemManager.HasCycle(node.IndexId);
|
||||
var hasCycle = await GroupProfileManager.HasCycle(node);
|
||||
if (hasCycle)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
var (childProfiles, profileGroupItem) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId);
|
||||
var (childProfiles, profileExtraItem) = await GroupProfileManager.GetChildProfileItems(node);
|
||||
if (childProfiles.Count <= 0)
|
||||
{
|
||||
return -1;
|
||||
|
|
@ -467,13 +470,14 @@ public partial class CoreConfigSingboxService
|
|||
switch (node.ConfigType)
|
||||
{
|
||||
case EConfigType.PolicyGroup:
|
||||
var multipleLoad = profileExtraItem?.MultipleLoad ?? EMultipleLoad.LeastPing;
|
||||
if (ignoreOriginChain)
|
||||
{
|
||||
await GenOutboundsList(childProfiles, singboxConfig, profileGroupItem.MultipleLoad, baseTagName);
|
||||
await GenOutboundsList(childProfiles, singboxConfig, multipleLoad, baseTagName);
|
||||
}
|
||||
else
|
||||
{
|
||||
await GenOutboundsListWithChain(childProfiles, singboxConfig, profileGroupItem.MultipleLoad, baseTagName);
|
||||
await GenOutboundsListWithChain(childProfiles, singboxConfig, multipleLoad, baseTagName);
|
||||
}
|
||||
|
||||
break;
|
||||
|
|
@ -585,7 +589,7 @@ public partial class CoreConfigSingboxService
|
|||
|
||||
if (node.ConfigType.IsGroupType())
|
||||
{
|
||||
var (childProfiles, profileGroupItem) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId);
|
||||
var (childProfiles, profileExtraItem) = await GroupProfileManager.GetChildProfileItems(node);
|
||||
if (childProfiles.Count <= 0)
|
||||
{
|
||||
continue;
|
||||
|
|
@ -594,7 +598,8 @@ public partial class CoreConfigSingboxService
|
|||
var ret = node.ConfigType switch
|
||||
{
|
||||
EConfigType.PolicyGroup =>
|
||||
await GenOutboundsListWithChain(childProfiles, singboxConfig, profileGroupItem.MultipleLoad, childBaseTagName),
|
||||
await GenOutboundsListWithChain(childProfiles, singboxConfig,
|
||||
profileExtraItem?.MultipleLoad ?? EMultipleLoad.LeastPing, childBaseTagName),
|
||||
EConfigType.ProxyChain =>
|
||||
await GenChainOutboundsList(childProfiles, singboxConfig, childBaseTagName),
|
||||
_ => throw new NotImplementedException()
|
||||
|
|
@ -763,7 +768,7 @@ public partial class CoreConfigSingboxService
|
|||
|
||||
if (node.ConfigType.IsGroupType())
|
||||
{
|
||||
var (childProfiles, profileGroupItem) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId);
|
||||
var (childProfiles, profileExtraItem) = await GroupProfileManager.GetChildProfileItems(node);
|
||||
if (childProfiles.Count <= 0)
|
||||
{
|
||||
continue;
|
||||
|
|
@ -772,7 +777,7 @@ public partial class CoreConfigSingboxService
|
|||
var ret = node.ConfigType switch
|
||||
{
|
||||
EConfigType.PolicyGroup =>
|
||||
await GenOutboundsList(childProfiles, singboxConfig, profileGroupItem.MultipleLoad, childBaseTagName),
|
||||
await GenOutboundsList(childProfiles, singboxConfig, profileExtraItem?.MultipleLoad ?? EMultipleLoad.LeastPing, childBaseTagName),
|
||||
EConfigType.ProxyChain =>
|
||||
await GenChainOutboundsList(childProfiles, singboxConfig, childBaseTagName),
|
||||
_ => throw new NotImplementedException()
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ public partial class CoreConfigV2rayService
|
|||
{
|
||||
try
|
||||
{
|
||||
var extraItem = node.GetExtraItem();
|
||||
var muxEnabled = node.MuxEnabled ?? _config.CoreBasicItem.MuxEnabled;
|
||||
switch (node.ConfigType)
|
||||
{
|
||||
|
|
@ -36,7 +37,7 @@ public partial class CoreConfigV2rayService
|
|||
}
|
||||
|
||||
usersItem.id = node.Id;
|
||||
usersItem.alterId = node.AlterId;
|
||||
usersItem.alterId = int.TryParse(extraItem?.AlterId, out var result) ? result : 0;
|
||||
usersItem.email = Global.UserEMail;
|
||||
if (Global.VmessSecurities.Contains(node.Security))
|
||||
{
|
||||
|
|
@ -142,13 +143,13 @@ public partial class CoreConfigV2rayService
|
|||
usersItem.email = Global.UserEMail;
|
||||
usersItem.encryption = node.Security;
|
||||
|
||||
if (node.Flow.IsNullOrEmpty())
|
||||
if (extraItem.Flow.IsNullOrEmpty())
|
||||
{
|
||||
await GenOutboundMux(node, outbound, muxEnabled, muxEnabled);
|
||||
}
|
||||
else
|
||||
{
|
||||
usersItem.flow = node.Flow;
|
||||
usersItem.flow = extraItem.Flow;
|
||||
await GenOutboundMux(node, outbound, false, muxEnabled);
|
||||
}
|
||||
outbound.settings.servers = null;
|
||||
|
|
@ -509,13 +510,15 @@ public partial class CoreConfigV2rayService
|
|||
break;
|
||||
|
||||
case "hysteria":
|
||||
var extraItem = node.GetExtraItem();
|
||||
var ports = extraItem?.Ports;
|
||||
HysteriaUdpHop4Ray? udpHop = null;
|
||||
if (node.Ports.IsNotEmpty() &&
|
||||
(node.Ports.Contains(':') || node.Ports.Contains('-') || node.Ports.Contains(',')))
|
||||
if (!ports.IsNullOrEmpty() &&
|
||||
(ports.Contains(':') || ports.Contains('-') || ports.Contains(',')))
|
||||
{
|
||||
udpHop = new()
|
||||
{
|
||||
ports = node.Ports.Replace(':', '-'),
|
||||
ports = ports.Replace(':', '-'),
|
||||
interval = _config.HysteriaItem.HopInterval > 0
|
||||
? _config.HysteriaItem.HopInterval
|
||||
: null,
|
||||
|
|
@ -592,13 +595,13 @@ public partial class CoreConfigV2rayService
|
|||
{
|
||||
return -1;
|
||||
}
|
||||
var hasCycle = ProfileGroupItemManager.HasCycle(node.IndexId);
|
||||
var hasCycle = await GroupProfileManager.HasCycle(node);
|
||||
if (hasCycle)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
var (childProfiles, profileGroupItem) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId);
|
||||
var (childProfiles, profileExtraItem) = await GroupProfileManager.GetChildProfileItems(node);
|
||||
if (childProfiles.Count <= 0)
|
||||
{
|
||||
return -1;
|
||||
|
|
@ -627,8 +630,9 @@ public partial class CoreConfigV2rayService
|
|||
//add balancers
|
||||
if (node.ConfigType == EConfigType.PolicyGroup)
|
||||
{
|
||||
await GenObservatory(v2rayConfig, profileGroupItem.MultipleLoad, baseTagName);
|
||||
await GenBalancer(v2rayConfig, profileGroupItem.MultipleLoad, baseTagName);
|
||||
var multipleLoad = profileExtraItem?.MultipleLoad ?? EMultipleLoad.LeastPing;
|
||||
await GenObservatory(v2rayConfig, multipleLoad, baseTagName);
|
||||
await GenBalancer(v2rayConfig, multipleLoad, baseTagName);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -737,7 +741,7 @@ public partial class CoreConfigV2rayService
|
|||
|
||||
if (node.ConfigType.IsGroupType())
|
||||
{
|
||||
var (childProfiles, _) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId);
|
||||
var (childProfiles, _) = await GroupProfileManager.GetChildProfileItems(node);
|
||||
if (childProfiles.Count <= 0)
|
||||
{
|
||||
continue;
|
||||
|
|
@ -891,7 +895,7 @@ public partial class CoreConfigV2rayService
|
|||
|
||||
if (node.ConfigType.IsGroupType())
|
||||
{
|
||||
var (childProfiles, _) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId);
|
||||
var (childProfiles, _) = await GroupProfileManager.GetChildProfileItems(node);
|
||||
if (childProfiles.Count <= 0)
|
||||
{
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -79,8 +79,8 @@ public class AddGroupServerViewModel : MyReactiveObject
|
|||
|
||||
public async Task Init()
|
||||
{
|
||||
ProfileGroupItemManager.Instance.TryGet(SelectedSource.IndexId, out var profileGroup);
|
||||
PolicyGroupType = (profileGroup?.MultipleLoad ?? EMultipleLoad.LeastPing) switch
|
||||
var extra = SelectedSource.GetExtraItem();
|
||||
PolicyGroupType = (extra?.MultipleLoad ?? EMultipleLoad.LeastPing) switch
|
||||
{
|
||||
EMultipleLoad.LeastPing => ResUI.TbLeastPing,
|
||||
EMultipleLoad.Fallback => ResUI.TbFallback,
|
||||
|
|
@ -93,22 +93,18 @@ public class AddGroupServerViewModel : MyReactiveObject
|
|||
var subs = await AppManager.Instance.SubItems();
|
||||
subs.Add(new SubItem());
|
||||
SubItems.AddRange(subs);
|
||||
SelectedSubItem = SubItems.Where(s => s.Id == profileGroup?.SubChildItems).FirstOrDefault();
|
||||
Filter = profileGroup?.Filter;
|
||||
SelectedSubItem = SubItems.FirstOrDefault(s => s.Id == extra?.SubChildItems);
|
||||
Filter = extra?.Filter;
|
||||
|
||||
var childItemMulti = ProfileGroupItemManager.Instance.GetOrCreateAndMarkDirty(SelectedSource?.IndexId);
|
||||
if (childItemMulti != null)
|
||||
var childIndexIds = Utils.String2List(extra?.ChildItems) ?? [];
|
||||
foreach (var item in childIndexIds)
|
||||
{
|
||||
var childIndexIds = Utils.String2List(childItemMulti.ChildItems) ?? [];
|
||||
foreach (var item in childIndexIds)
|
||||
var child = await AppManager.Instance.GetProfileItem(item);
|
||||
if (child == null)
|
||||
{
|
||||
var child = await AppManager.Instance.GetProfileItem(item);
|
||||
if (child == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
ChildItemsObs.Add(child);
|
||||
continue;
|
||||
}
|
||||
ChildItemsObs.Add(child);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -205,18 +201,11 @@ public class AddGroupServerViewModel : MyReactiveObject
|
|||
{
|
||||
return;
|
||||
}
|
||||
var childIndexIds = new List<string>();
|
||||
foreach (var item in ChildItemsObs)
|
||||
{
|
||||
if (item.IndexId.IsNullOrEmpty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
childIndexIds.Add(item.IndexId);
|
||||
}
|
||||
var profileGroup = ProfileGroupItemManager.Instance.GetOrCreateAndMarkDirty(SelectedSource.IndexId);
|
||||
profileGroup.ChildItems = Utils.List2String(childIndexIds);
|
||||
profileGroup.MultipleLoad = PolicyGroupType switch
|
||||
|
||||
var extra = SelectedSource.GetExtraItem();
|
||||
extra.ChildItems =
|
||||
Utils.List2String(ChildItemsObs.Where(s => !s.IndexId.IsNullOrEmpty()).Select(s => s.IndexId).ToList());
|
||||
extra.MultipleLoad = PolicyGroupType switch
|
||||
{
|
||||
var s when s == ResUI.TbLeastPing => EMultipleLoad.LeastPing,
|
||||
var s when s == ResUI.TbFallback => EMultipleLoad.Fallback,
|
||||
|
|
@ -226,17 +215,19 @@ public class AddGroupServerViewModel : MyReactiveObject
|
|||
_ => EMultipleLoad.LeastPing,
|
||||
};
|
||||
|
||||
profileGroup.SubChildItems = SelectedSubItem?.Id;
|
||||
profileGroup.Filter = Filter;
|
||||
extra.SubChildItems = SelectedSubItem?.Id;
|
||||
extra.Filter = Filter;
|
||||
|
||||
var hasCycle = ProfileGroupItemManager.HasCycle(profileGroup.IndexId);
|
||||
var hasCycle = await GroupProfileManager.HasCycle(SelectedSource.IndexId, extra);
|
||||
if (hasCycle)
|
||||
{
|
||||
NoticeManager.Instance.Enqueue(string.Format(ResUI.GroupSelfReference, remarks));
|
||||
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);
|
||||
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
||||
|
|
|
|||
|
|
@ -17,6 +17,15 @@ public class AddServerViewModel : MyReactiveObject
|
|||
[Reactive]
|
||||
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> FetchCertChainCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
||||
|
|
@ -63,6 +72,11 @@ public class AddServerViewModel : MyReactiveObject
|
|||
CoreType = SelectedSource?.CoreType?.ToString();
|
||||
Cert = SelectedSource?.Cert?.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()
|
||||
|
|
@ -109,6 +123,11 @@ public class AddServerViewModel : MyReactiveObject
|
|||
SelectedSource.CoreType = CoreType.IsNullOrEmpty() ? null : (ECoreType)Enum.Parse(typeof(ECoreType), CoreType);
|
||||
SelectedSource.Cert = Cert.IsNullOrEmpty() ? string.Empty : Cert;
|
||||
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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
|||
{
|
||||
case EConfigType.VMess:
|
||||
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.MuxEnabled, v => v.togmuxEnabled.IsChecked).DisposeWith(disposables);
|
||||
break;
|
||||
|
|
@ -139,21 +139,21 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
|||
|
||||
case EConfigType.VLESS:
|
||||
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.MuxEnabled, v => v.togmuxEnabled5.IsChecked).DisposeWith(disposables);
|
||||
break;
|
||||
|
||||
case EConfigType.Trojan:
|
||||
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);
|
||||
break;
|
||||
|
||||
case EConfigType.Hysteria2:
|
||||
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.Ports, v => v.txtPorts7.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.Ports, v => v.txtPorts7.Text).DisposeWith(disposables);
|
||||
break;
|
||||
|
||||
case EConfigType.TUIC:
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ public partial class AddServerWindow
|
|||
{
|
||||
case EConfigType.VMess:
|
||||
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.MuxEnabled, v => v.togmuxEnabled.IsChecked).DisposeWith(disposables);
|
||||
break;
|
||||
|
|
@ -134,21 +134,21 @@ public partial class AddServerWindow
|
|||
|
||||
case EConfigType.VLESS:
|
||||
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.MuxEnabled, v => v.togmuxEnabled5.IsChecked).DisposeWith(disposables);
|
||||
break;
|
||||
|
||||
case EConfigType.Trojan:
|
||||
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);
|
||||
break;
|
||||
|
||||
case EConfigType.Hysteria2:
|
||||
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.Ports, v => v.txtPorts7.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.Ports, v => v.txtPorts7.Text).DisposeWith(disposables);
|
||||
break;
|
||||
|
||||
case EConfigType.TUIC:
|
||||
|
|
|
|||
Loading…
Reference in a new issue