From 7a58e783816d6de9e9429fdac621db12c742fe2d Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Fri, 6 Feb 2026 11:45:26 +0800 Subject: [PATCH] Refactor profile migration and add group handling --- v2rayN/ServiceLib/Manager/AppManager.cs | 266 ++++++++++++++++-------- 1 file changed, 176 insertions(+), 90 deletions(-) diff --git a/v2rayN/ServiceLib/Manager/AppManager.cs b/v2rayN/ServiceLib/Manager/AppManager.cs index 576af0d0..83633c0f 100644 --- a/v2rayN/ServiceLib/Manager/AppManager.cs +++ b/v2rayN/ServiceLib/Manager/AppManager.cs @@ -264,107 +264,26 @@ public sealed class AppManager public async Task MigrateProfileExtra() { -#pragma warning disable CS0618 - var list = await SQLiteHelper.Instance.TableAsync().ToListAsync(); - var groupItems = new ConcurrentDictionary(list.Where(t => !string.IsNullOrEmpty(t.IndexId)).ToDictionary(t => t.IndexId!)); + await MigrateProfileExtraGroup(); - const int pageSize = 500; +#pragma warning disable CS0618 + + const int pageSize = 100; var offset = 0; while (true) { - var sql = $"SELECT * FROM ProfileItem WHERE ConfigVersion < 3 LIMIT {pageSize} OFFSET {offset}"; + var sql = $"SELECT * FROM ProfileItem " + + $"WHERE ConfigVersion < 3 " + + $"AND ConfigType NOT IN ({(int)EConfigType.PolicyGroup}, {(int)EConfigType.ProxyChain}) " + + $"LIMIT {pageSize} OFFSET {offset}"; var batch = await SQLiteHelper.Instance.QueryAsync(sql); if (batch is null || batch.Count == 0) { break; } - var batchSuccessCount = 0; - foreach (var item in batch) - { - try - { - var extra = item.GetProtocolExtra(); - - if (item.ConfigType is EConfigType.PolicyGroup or EConfigType.ProxyChain) - { - extra = extra with { GroupType = nameof(item.ConfigType) }; - groupItems.TryGetValue(item.IndexId, out var groupItem); - if (groupItem != null && !groupItem.NotHasChild()) - { - extra = extra with - { - ChildItems = groupItem.ChildItems, - SubChildItems = groupItem.SubChildItems, - Filter = groupItem.Filter, - MultipleLoad = groupItem.MultipleLoad, - }; - } - } - - switch (item.ConfigType) - { - case EConfigType.Shadowsocks: - extra = extra with { SsMethod = item.Security.NullIfEmpty() }; - break; - case EConfigType.VMess: - extra = extra with - { - AlterId = item.AlterId.ToString(), - VmessSecurity = item.Security.NullIfEmpty(), - }; - break; - case EConfigType.VLESS: - extra = extra with - { - Flow = item.Flow.NullIfEmpty(), - VlessEncryption = item.Security, - }; - break; - case EConfigType.Hysteria2: - extra = extra with - { - SalamanderPass = item.Path.NullIfEmpty(), - Ports = item.Ports.NullIfEmpty(), - UpMbps = _config.HysteriaItem.UpMbps, - DownMbps = _config.HysteriaItem.DownMbps, - HopInterval = _config.HysteriaItem.HopInterval.ToString(), - }; - break; - case EConfigType.TUIC: - item.Username = item.Id; - item.Id = item.Security; - item.Password = item.Security; - break; - case EConfigType.HTTP: - case EConfigType.SOCKS: - item.Username = item.Security; - break; - case EConfigType.WireGuard: - extra = extra with - { - WgPublicKey = item.PublicKey.NullIfEmpty(), - WgInterfaceAddress = item.RequestHost.NullIfEmpty(), - WgReserved = item.Path.NullIfEmpty(), - WgMtu = int.TryParse(item.ShortId, out var mtu) ? mtu : 1280 - }; - break; - } - - item.SetProtocolExtra(extra); - - item.Password = item.Id; - - item.ConfigVersion = 3; - await SQLiteHelper.Instance.UpdateAsync(item); - batchSuccessCount++; - } - catch (Exception ex) - { - Logging.SaveLog($"MigrateProfileExtra Error: {ex}"); - } - } + var batchSuccessCount = await MigrateProfileExtraSub(batch); // Only increment offset by the number of failed items that remain in the result set // Successfully updated items are automatically excluded from future queries due to ConfigVersion = 3 @@ -375,6 +294,173 @@ public sealed class AppManager #pragma warning restore CS0618 } + private async Task MigrateProfileExtraSub(List batch) + { + var updateProfileItems = new List(); + + foreach (var item in batch) + { + try + { + var extra = item.GetProtocolExtra(); + switch (item.ConfigType) + { + case EConfigType.Shadowsocks: + extra = extra with { SsMethod = item.Security.NullIfEmpty() }; + break; + + case EConfigType.VMess: + extra = extra with + { + AlterId = item.AlterId.ToString(), + VmessSecurity = item.Security.NullIfEmpty(), + }; + break; + + case EConfigType.VLESS: + extra = extra with + { + Flow = item.Flow.NullIfEmpty(), + VlessEncryption = item.Security, + }; + break; + + case EConfigType.Hysteria2: + extra = extra with + { + SalamanderPass = item.Path.NullIfEmpty(), + Ports = item.Ports.NullIfEmpty(), + UpMbps = _config.HysteriaItem.UpMbps, + DownMbps = _config.HysteriaItem.DownMbps, + HopInterval = _config.HysteriaItem.HopInterval.ToString(), + }; + break; + + case EConfigType.TUIC: + item.Username = item.Id; + item.Id = item.Security; + item.Password = item.Security; + break; + + case EConfigType.HTTP: + case EConfigType.SOCKS: + item.Username = item.Security; + break; + + case EConfigType.WireGuard: + extra = extra with + { + WgPublicKey = item.PublicKey.NullIfEmpty(), + WgInterfaceAddress = item.RequestHost.NullIfEmpty(), + WgReserved = item.Path.NullIfEmpty(), + WgMtu = int.TryParse(item.ShortId, out var mtu) ? mtu : 1280 + }; + break; + } + + item.SetProtocolExtra(extra); + + item.Password = item.Id; + + item.ConfigVersion = 3; + + updateProfileItems.Add(item); + } + catch (Exception ex) + { + Logging.SaveLog($"MigrateProfileExtra Error: {ex}"); + } + } + + if (updateProfileItems.Count > 0) + { + try + { + var count = await SQLiteHelper.Instance.UpdateAllAsync(updateProfileItems); + return count; + } + catch (Exception ex) + { + Logging.SaveLog($"MigrateProfileExtraGroup update error: {ex}"); + return 0; + } + } + else + { + return 0; + } + } + + private async Task MigrateProfileExtraGroup() + { +#pragma warning disable CS0618 + var list = await SQLiteHelper.Instance.TableAsync().ToListAsync(); + var groupItems = new ConcurrentDictionary(list.Where(t => !string.IsNullOrEmpty(t.IndexId)).ToDictionary(t => t.IndexId!)); + + var sql = $"SELECT * FROM ProfileItem WHERE ConfigVersion < 3 AND ConfigType IN ({(int)EConfigType.PolicyGroup}, {(int)EConfigType.ProxyChain})"; + var items = await SQLiteHelper.Instance.QueryAsync(sql); + + if (items is null || items.Count == 0) + { + Logging.SaveLog("MigrateProfileExtraGroup: No items to migrate."); + return true; + } + + Logging.SaveLog($"MigrateProfileExtraGroup: Found {items.Count} group items to migrate."); + + var updateProfileItems = new List(); + + foreach (var item in items) + { + try + { + var extra = item.GetProtocolExtra(); + + extra = extra with { GroupType = nameof(item.ConfigType) }; + groupItems.TryGetValue(item.IndexId, out var groupItem); + if (groupItem != null && !groupItem.NotHasChild()) + { + extra = extra with + { + ChildItems = groupItem.ChildItems, + SubChildItems = groupItem.SubChildItems, + Filter = groupItem.Filter, + MultipleLoad = groupItem.MultipleLoad, + }; + } + + item.SetProtocolExtra(extra); + + item.ConfigVersion = 3; + updateProfileItems.Add(item); + } + catch (Exception ex) + { + Logging.SaveLog($"MigrateProfileExtraGroup item error [{item.IndexId}]: {ex}"); + } + } + + if (updateProfileItems.Count > 0) + { + try + { + var count = await SQLiteHelper.Instance.UpdateAllAsync(updateProfileItems); + Logging.SaveLog($"MigrateProfileExtraGroup: Successfully updated {updateProfileItems.Count} items."); + return updateProfileItems.Count == count; + } + catch (Exception ex) + { + Logging.SaveLog($"MigrateProfileExtraGroup update error: {ex}"); + return false; + } + } + + return true; + + //await ProfileGroupItemManager.Instance.ClearAll(); +#pragma warning restore CS0618 + } + #endregion SqliteHelper #region Core Type