mirror of
https://github.com/2dust/v2rayN.git
synced 2025-10-13 20:09:12 +00:00
Multi Profile
This commit is contained in:
parent
18ac76e683
commit
5c4f485471
10 changed files with 248 additions and 8 deletions
|
@ -12,5 +12,9 @@ public enum EConfigType
|
||||||
TUIC = 8,
|
TUIC = 8,
|
||||||
WireGuard = 9,
|
WireGuard = 9,
|
||||||
HTTP = 10,
|
HTTP = 10,
|
||||||
Anytls = 11
|
Anytls = 11,
|
||||||
|
|
||||||
|
Group = 1000,
|
||||||
|
PolicyGroup = 1001,
|
||||||
|
ProxyChain = 1002,
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,8 @@ namespace ServiceLib.Enums;
|
||||||
|
|
||||||
public enum EMultipleLoad
|
public enum EMultipleLoad
|
||||||
{
|
{
|
||||||
|
LeastPing,
|
||||||
Random,
|
Random,
|
||||||
RoundRobin,
|
RoundRobin,
|
||||||
LeastPing,
|
|
||||||
LeastLoad
|
LeastLoad
|
||||||
}
|
}
|
||||||
|
|
|
@ -357,6 +357,11 @@ public static class ConfigHandler
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (profileItem.ConfigType > EConfigType.Group)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
@ -1074,6 +1079,35 @@ 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();
|
||||||
|
}
|
||||||
|
if (maxSort > 0)
|
||||||
|
{
|
||||||
|
ProfileExManager.Instance.SetSort(profileItem.IndexId, maxSort + 1);
|
||||||
|
}
|
||||||
|
if (toFile)
|
||||||
|
{
|
||||||
|
await SQLiteHelper.Instance.ReplaceAsync(profileItem);
|
||||||
|
if (profileGroupItem != null)
|
||||||
|
{
|
||||||
|
profileGroupItem.ParentIndexId = 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
|
||||||
|
@ -1207,7 +1241,7 @@ public static class ConfigHandler
|
||||||
public static async Task<ProfileItem?> GetPreSocksItem(Config config, ProfileItem node, ECoreType coreType)
|
public static async Task<ProfileItem?> GetPreSocksItem(Config config, ProfileItem node, ECoreType coreType)
|
||||||
{
|
{
|
||||||
ProfileItem? itemSocks = null;
|
ProfileItem? itemSocks = null;
|
||||||
if (node.ConfigType != EConfigType.Custom && coreType != ECoreType.sing_box && config.TunModeItem.EnableTun)
|
if (node.ConfigType != EConfigType.Custom && node.ConfigType < EConfigType.Group && coreType != ECoreType.sing_box && config.TunModeItem.EnableTun)
|
||||||
{
|
{
|
||||||
itemSocks = new ProfileItem()
|
itemSocks = new ProfileItem()
|
||||||
{
|
{
|
||||||
|
@ -1218,7 +1252,7 @@ public static class ConfigHandler
|
||||||
Port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks)
|
Port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if (node.ConfigType == EConfigType.Custom && node.PreSocksPort > 0)
|
else if (node.ConfigType == EConfigType.Custom && node.ConfigType < EConfigType.Group && node.PreSocksPort > 0)
|
||||||
{
|
{
|
||||||
var preCoreType = config.RunningCoreType = config.TunModeItem.EnableTun ? ECoreType.sing_box : ECoreType.Xray;
|
var preCoreType = config.RunningCoreType = config.TunModeItem.EnableTun ? ECoreType.sing_box : ECoreType.Xray;
|
||||||
itemSocks = new ProfileItem()
|
itemSocks = new ProfileItem()
|
||||||
|
|
|
@ -67,6 +67,7 @@ public sealed class AppManager
|
||||||
SQLiteHelper.Instance.CreateTable<ProfileExItem>();
|
SQLiteHelper.Instance.CreateTable<ProfileExItem>();
|
||||||
SQLiteHelper.Instance.CreateTable<DNSItem>();
|
SQLiteHelper.Instance.CreateTable<DNSItem>();
|
||||||
SQLiteHelper.Instance.CreateTable<FullConfigTemplateItem>();
|
SQLiteHelper.Instance.CreateTable<FullConfigTemplateItem>();
|
||||||
|
SQLiteHelper.Instance.CreateTable<ProfileGroupItem>();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,6 +102,7 @@ public sealed class AppManager
|
||||||
|
|
||||||
await ConfigHandler.SaveConfig(_config);
|
await ConfigHandler.SaveConfig(_config);
|
||||||
await ProfileExManager.Instance.SaveTo();
|
await ProfileExManager.Instance.SaveTo();
|
||||||
|
await ProfileGroupItemManager.Instance.SaveTo();
|
||||||
await StatisticsManager.Instance.SaveTo();
|
await StatisticsManager.Instance.SaveTo();
|
||||||
await CoreManager.Instance.CoreStop();
|
await CoreManager.Instance.CoreStop();
|
||||||
StatisticsManager.Instance.Close();
|
StatisticsManager.Instance.Close();
|
||||||
|
@ -219,6 +221,15 @@ 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 parentIndexId)
|
||||||
|
{
|
||||||
|
if (parentIndexId.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return await SQLiteHelper.Instance.TableAsync<ProfileGroupItem>().FirstOrDefaultAsync(it => it.ParentIndexId == parentIndexId);
|
||||||
|
}
|
||||||
|
|
||||||
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();
|
||||||
|
|
156
v2rayN/ServiceLib/Manager/ProfileGroupItemManager.cs
Normal file
156
v2rayN/ServiceLib/Manager/ProfileGroupItemManager.cs
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
|
namespace ServiceLib.Manager;
|
||||||
|
|
||||||
|
public class ProfileGroupItemManager
|
||||||
|
{
|
||||||
|
private static readonly Lazy<ProfileGroupItemManager> _instance = new(() => new());
|
||||||
|
private ConcurrentDictionary<string, ProfileGroupItem> _items = new();
|
||||||
|
|
||||||
|
public static ProfileGroupItemManager Instance => _instance.Value;
|
||||||
|
private static readonly string _tag = "ProfileGroupItemManager";
|
||||||
|
|
||||||
|
private ProfileGroupItemManager()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Init()
|
||||||
|
{
|
||||||
|
await InitData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<ConcurrentBag<ProfileGroupItem>> GetProfileGroupItemList()
|
||||||
|
{
|
||||||
|
var bag = new ConcurrentBag<ProfileGroupItem>(_items.Values);
|
||||||
|
return Task.FromResult(bag);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task InitData()
|
||||||
|
{
|
||||||
|
await SQLiteHelper.Instance.ExecuteAsync($"delete from ProfileGroupItem where parentIndexId not in ( select indexId from ProfileItem )");
|
||||||
|
|
||||||
|
var list = await SQLiteHelper.Instance.TableAsync<ProfileGroupItem>().ToListAsync();
|
||||||
|
_items = new ConcurrentDictionary<string, ProfileGroupItem>(list.Where(t => !string.IsNullOrEmpty(t.ParentIndexId)).ToDictionary(t => t.ParentIndexId!));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ProfileGroupItem AddProfileGroupItem(string indexId)
|
||||||
|
{
|
||||||
|
var profileGroupItem = new ProfileGroupItem()
|
||||||
|
{
|
||||||
|
ParentIndexId = indexId,
|
||||||
|
ChildItems = string.Empty,
|
||||||
|
MultipleLoad = EMultipleLoad.LeastPing
|
||||||
|
};
|
||||||
|
|
||||||
|
_items[indexId] = profileGroupItem;
|
||||||
|
return profileGroupItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ProfileGroupItem GetProfileGroupItem(string indexId)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(indexId))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(indexId));
|
||||||
|
}
|
||||||
|
|
||||||
|
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.ParentIndexId)).ToDictionary(t => t.ParentIndexId!);
|
||||||
|
|
||||||
|
var lstInserts = new List<ProfileGroupItem>();
|
||||||
|
var lstUpdates = new List<ProfileGroupItem>();
|
||||||
|
|
||||||
|
foreach (var item in _items.Values)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(item.ParentIndexId))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existsMap.ContainsKey(item.ParentIndexId))
|
||||||
|
{
|
||||||
|
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.ParentIndexId))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("ParentIndexId required", nameof(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
_items[item.ParentIndexId] = item;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var lst = await SQLiteHelper.Instance.TableAsync<ProfileGroupItem>().Where(t => t.ParentIndexId == item.ParentIndexId).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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -35,6 +35,7 @@ public class TaskManager
|
||||||
|
|
||||||
await ConfigHandler.SaveConfig(_config);
|
await ConfigHandler.SaveConfig(_config);
|
||||||
await ProfileExManager.Instance.SaveTo();
|
await ProfileExManager.Instance.SaveTo();
|
||||||
|
await ProfileGroupItemManager.Instance.SaveTo();
|
||||||
}
|
}
|
||||||
|
|
||||||
//Execute once 1 hour
|
//Execute once 1 hour
|
||||||
|
|
13
v2rayN/ServiceLib/Models/ProfileGroupItem.cs
Normal file
13
v2rayN/ServiceLib/Models/ProfileGroupItem.cs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
using SQLite;
|
||||||
|
namespace ServiceLib.Models;
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class ProfileGroupItem
|
||||||
|
{
|
||||||
|
[PrimaryKey]
|
||||||
|
public string ParentIndexId { get; set; }
|
||||||
|
|
||||||
|
public string ChildItems { get; set; }
|
||||||
|
|
||||||
|
public EMultipleLoad MultipleLoad { get; set; } = EMultipleLoad.LeastPing;
|
||||||
|
}
|
|
@ -39,11 +39,14 @@ public class ProfileItem : ReactiveObject
|
||||||
> 1 => $"***{arrAddr.Last()}",
|
> 1 => $"***{arrAddr.Last()}",
|
||||||
_ => Address
|
_ => Address
|
||||||
};
|
};
|
||||||
summary += ConfigType switch
|
if (ConfigType is EConfigType.Custom or > EConfigType.Group)
|
||||||
{
|
{
|
||||||
EConfigType.Custom => $"[{CoreType.ToString()}]{Remarks}",
|
summary += $"[{CoreType.ToString()}]{Remarks}";
|
||||||
_ => $"{Remarks}({addr}:{Port})"
|
}
|
||||||
};
|
else
|
||||||
|
{
|
||||||
|
summary += $"{Remarks}({addr}:{Port})";
|
||||||
|
}
|
||||||
return summary;
|
return summary;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
17
v2rayN/ServiceLib/ViewModels/AddGroupServerViewModel.cs
Normal file
17
v2rayN/ServiceLib/ViewModels/AddGroupServerViewModel.cs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
using System.Reactive;
|
||||||
|
using ReactiveUI;
|
||||||
|
using ReactiveUI.Fody.Helpers;
|
||||||
|
|
||||||
|
namespace ServiceLib.ViewModels;
|
||||||
|
|
||||||
|
public class AddGroupServerViewModel : MyReactiveObject
|
||||||
|
{
|
||||||
|
[Reactive]
|
||||||
|
public ProfileItem SelectedSource { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public IList<ProfileItem> SelectedChildren { get; set; }
|
||||||
|
|
||||||
|
//[Reactive]
|
||||||
|
//public
|
||||||
|
}
|
|
@ -228,6 +228,7 @@ public class MainWindowViewModel : MyReactiveObject
|
||||||
await ConfigHandler.InitBuiltinDNS(_config);
|
await ConfigHandler.InitBuiltinDNS(_config);
|
||||||
await ConfigHandler.InitBuiltinFullConfigTemplate(_config);
|
await ConfigHandler.InitBuiltinFullConfigTemplate(_config);
|
||||||
await ProfileExManager.Instance.Init();
|
await ProfileExManager.Instance.Init();
|
||||||
|
await ProfileGroupItemManager.Instance.Init();
|
||||||
await CoreManager.Instance.Init(_config, UpdateHandler);
|
await CoreManager.Instance.Init(_config, UpdateHandler);
|
||||||
TaskManager.Instance.RegUpdateTask(_config, UpdateTaskHandler);
|
TaskManager.Instance.RegUpdateTask(_config, UpdateTaskHandler);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue