mirror of
https://github.com/2dust/v2rayN.git
synced 2026-02-18 08:13:02 +00:00
Refactor profile item config (#8659)
Some checks are pending
release Linux / build (Release) (push) Waiting to run
release Linux / rpm (push) Blocked by required conditions
release macOS / build (Release) (push) Waiting to run
release Windows desktop (Avalonia UI) / build (Release) (push) Waiting to run
release Windows / build (Release) (push) Waiting to run
Some checks are pending
release Linux / build (Release) (push) Waiting to run
release Linux / rpm (push) Blocked by required conditions
release macOS / build (Release) (push) Waiting to run
release Windows desktop (Avalonia UI) / build (Release) (push) Waiting to run
release Windows / build (Release) (push) Waiting to run
* Refactor * Add hysteria2 bandwidth and hop interval support * Upgrade config version and rename * Refactor id and security * Refactor flow * Fix hy2 bbr * Fix warning CS0618 * Remove unused code * Fix hy2 migrate * Fix * Refactor * Refactor ProfileItem protocol extra handling * Refactor, use record instead of class * Hy2 SalamanderPass * Fix Tuic * Fix group * Fix Tuic * Fix hy2 Brutal Bandwidth * Clean Code * Fix * Support interval range * Add Username --------- Co-authored-by: 2dust <31833384+2dust@users.noreply.github.com>
This commit is contained in:
parent
d9843dc775
commit
677e81f9a7
40 changed files with 951 additions and 802 deletions
|
|
@ -90,6 +90,8 @@ public class Global
|
||||||
public const string SingboxFakeDNSTag = "fake_dns";
|
public const string SingboxFakeDNSTag = "fake_dns";
|
||||||
public const string SingboxEchDNSTag = "ech_dns";
|
public const string SingboxEchDNSTag = "ech_dns";
|
||||||
|
|
||||||
|
public const int Hysteria2DefaultHopInt = 10;
|
||||||
|
|
||||||
public static readonly List<string> IEProxyProtocols =
|
public static readonly List<string> IEProxyProtocols =
|
||||||
[
|
[
|
||||||
"{ip}:{http_port}",
|
"{ip}:{http_port}",
|
||||||
|
|
|
||||||
|
|
@ -230,12 +230,8 @@ public static class ConfigHandler
|
||||||
item.Remarks = profileItem.Remarks;
|
item.Remarks = profileItem.Remarks;
|
||||||
item.Address = profileItem.Address;
|
item.Address = profileItem.Address;
|
||||||
item.Port = profileItem.Port;
|
item.Port = profileItem.Port;
|
||||||
item.Ports = profileItem.Ports;
|
|
||||||
|
|
||||||
item.Id = profileItem.Id;
|
item.Password = profileItem.Password;
|
||||||
item.AlterId = profileItem.AlterId;
|
|
||||||
item.Security = profileItem.Security;
|
|
||||||
item.Flow = profileItem.Flow;
|
|
||||||
|
|
||||||
item.Network = profileItem.Network;
|
item.Network = profileItem.Network;
|
||||||
item.HeaderType = profileItem.HeaderType;
|
item.HeaderType = profileItem.HeaderType;
|
||||||
|
|
@ -258,6 +254,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.ProtoExtra = profileItem.ProtoExtra;
|
||||||
}
|
}
|
||||||
|
|
||||||
var ret = item.ConfigType switch
|
var ret = item.ConfigType switch
|
||||||
|
|
@ -290,19 +287,22 @@ public static class ConfigHandler
|
||||||
profileItem.ConfigType = EConfigType.VMess;
|
profileItem.ConfigType = EConfigType.VMess;
|
||||||
|
|
||||||
profileItem.Address = profileItem.Address.TrimEx();
|
profileItem.Address = profileItem.Address.TrimEx();
|
||||||
profileItem.Id = profileItem.Id.TrimEx();
|
profileItem.Password = profileItem.Password.TrimEx();
|
||||||
profileItem.Security = profileItem.Security.TrimEx();
|
profileItem.SetProtocolExtra(profileItem.GetProtocolExtra() with
|
||||||
|
{
|
||||||
|
VmessSecurity = profileItem.GetProtocolExtra().VmessSecurity?.TrimEx()
|
||||||
|
});
|
||||||
profileItem.Network = profileItem.Network.TrimEx();
|
profileItem.Network = profileItem.Network.TrimEx();
|
||||||
profileItem.HeaderType = profileItem.HeaderType.TrimEx();
|
profileItem.HeaderType = profileItem.HeaderType.TrimEx();
|
||||||
profileItem.RequestHost = profileItem.RequestHost.TrimEx();
|
profileItem.RequestHost = profileItem.RequestHost.TrimEx();
|
||||||
profileItem.Path = profileItem.Path.TrimEx();
|
profileItem.Path = profileItem.Path.TrimEx();
|
||||||
profileItem.StreamSecurity = profileItem.StreamSecurity.TrimEx();
|
profileItem.StreamSecurity = profileItem.StreamSecurity.TrimEx();
|
||||||
|
|
||||||
if (!Global.VmessSecurities.Contains(profileItem.Security))
|
if (!Global.VmessSecurities.Contains(profileItem.GetProtocolExtra().VmessSecurity))
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (profileItem.Id.IsNullOrEmpty())
|
if (profileItem.Password.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -360,11 +360,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);
|
||||||
|
|
@ -610,14 +605,17 @@ public static class ConfigHandler
|
||||||
profileItem.ConfigType = EConfigType.Shadowsocks;
|
profileItem.ConfigType = EConfigType.Shadowsocks;
|
||||||
|
|
||||||
profileItem.Address = profileItem.Address.TrimEx();
|
profileItem.Address = profileItem.Address.TrimEx();
|
||||||
profileItem.Id = profileItem.Id.TrimEx();
|
profileItem.Password = profileItem.Password.TrimEx();
|
||||||
profileItem.Security = profileItem.Security.TrimEx();
|
profileItem.SetProtocolExtra(profileItem.GetProtocolExtra() with
|
||||||
|
{
|
||||||
|
SsMethod = profileItem.GetProtocolExtra().SsMethod?.TrimEx()
|
||||||
|
});
|
||||||
|
|
||||||
if (!AppManager.Instance.GetShadowsocksSecurities(profileItem).Contains(profileItem.Security))
|
if (!AppManager.Instance.GetShadowsocksSecurities(profileItem).Contains(profileItem.GetProtocolExtra().SsMethod))
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (profileItem.Id.IsNullOrEmpty())
|
if (profileItem.Password.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -678,12 +676,12 @@ public static class ConfigHandler
|
||||||
profileItem.ConfigType = EConfigType.Trojan;
|
profileItem.ConfigType = EConfigType.Trojan;
|
||||||
|
|
||||||
profileItem.Address = profileItem.Address.TrimEx();
|
profileItem.Address = profileItem.Address.TrimEx();
|
||||||
profileItem.Id = profileItem.Id.TrimEx();
|
profileItem.Password = profileItem.Password.TrimEx();
|
||||||
if (profileItem.StreamSecurity.IsNullOrEmpty())
|
if (profileItem.StreamSecurity.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
profileItem.StreamSecurity = Global.StreamSecurity;
|
profileItem.StreamSecurity = Global.StreamSecurity;
|
||||||
}
|
}
|
||||||
if (profileItem.Id.IsNullOrEmpty())
|
if (profileItem.Password.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -708,18 +706,24 @@ public static class ConfigHandler
|
||||||
//profileItem.CoreType = ECoreType.sing_box;
|
//profileItem.CoreType = ECoreType.sing_box;
|
||||||
|
|
||||||
profileItem.Address = profileItem.Address.TrimEx();
|
profileItem.Address = profileItem.Address.TrimEx();
|
||||||
profileItem.Id = profileItem.Id.TrimEx();
|
profileItem.Password = profileItem.Password.TrimEx();
|
||||||
profileItem.Path = profileItem.Path.TrimEx();
|
|
||||||
profileItem.Network = string.Empty;
|
profileItem.Network = string.Empty;
|
||||||
|
|
||||||
if (profileItem.StreamSecurity.IsNullOrEmpty())
|
if (profileItem.StreamSecurity.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
profileItem.StreamSecurity = Global.StreamSecurity;
|
profileItem.StreamSecurity = Global.StreamSecurity;
|
||||||
}
|
}
|
||||||
if (profileItem.Id.IsNullOrEmpty())
|
if (profileItem.Password.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
profileItem.SetProtocolExtra(profileItem.GetProtocolExtra() with
|
||||||
|
{
|
||||||
|
SalamanderPass = profileItem.GetProtocolExtra().SalamanderPass?.TrimEx(),
|
||||||
|
UpMbps = profileItem.GetProtocolExtra().UpMbps is null or < 0 ? config.HysteriaItem.UpMbps : profileItem.GetProtocolExtra().UpMbps,
|
||||||
|
DownMbps = profileItem.GetProtocolExtra().DownMbps is null or < 0 ? config.HysteriaItem.DownMbps : profileItem.GetProtocolExtra().DownMbps,
|
||||||
|
HopInterval = profileItem.GetProtocolExtra().HopInterval?.TrimEx(),
|
||||||
|
});
|
||||||
|
|
||||||
await AddServerCommon(config, profileItem, toFile);
|
await AddServerCommon(config, profileItem, toFile);
|
||||||
|
|
||||||
|
|
@ -741,8 +745,8 @@ public static class ConfigHandler
|
||||||
profileItem.CoreType = ECoreType.sing_box;
|
profileItem.CoreType = ECoreType.sing_box;
|
||||||
|
|
||||||
profileItem.Address = profileItem.Address.TrimEx();
|
profileItem.Address = profileItem.Address.TrimEx();
|
||||||
profileItem.Id = profileItem.Id.TrimEx();
|
profileItem.Username = profileItem.Username.TrimEx();
|
||||||
profileItem.Security = profileItem.Security.TrimEx();
|
profileItem.Password = profileItem.Password.TrimEx();
|
||||||
profileItem.Network = string.Empty;
|
profileItem.Network = string.Empty;
|
||||||
|
|
||||||
if (!Global.TuicCongestionControls.Contains(profileItem.HeaderType))
|
if (!Global.TuicCongestionControls.Contains(profileItem.HeaderType))
|
||||||
|
|
@ -758,7 +762,7 @@ public static class ConfigHandler
|
||||||
{
|
{
|
||||||
profileItem.Alpn = "h3";
|
profileItem.Alpn = "h3";
|
||||||
}
|
}
|
||||||
if (profileItem.Id.IsNullOrEmpty())
|
if (profileItem.Password.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -781,17 +785,17 @@ public static class ConfigHandler
|
||||||
profileItem.ConfigType = EConfigType.WireGuard;
|
profileItem.ConfigType = EConfigType.WireGuard;
|
||||||
|
|
||||||
profileItem.Address = profileItem.Address.TrimEx();
|
profileItem.Address = profileItem.Address.TrimEx();
|
||||||
profileItem.Id = profileItem.Id.TrimEx();
|
profileItem.Password = profileItem.Password.TrimEx();
|
||||||
profileItem.PublicKey = profileItem.PublicKey.TrimEx();
|
profileItem.SetProtocolExtra(profileItem.GetProtocolExtra() with
|
||||||
profileItem.Path = profileItem.Path.TrimEx();
|
|
||||||
profileItem.RequestHost = profileItem.RequestHost.TrimEx();
|
|
||||||
profileItem.Network = string.Empty;
|
|
||||||
if (profileItem.ShortId.IsNullOrEmpty())
|
|
||||||
{
|
{
|
||||||
profileItem.ShortId = Global.TunMtus.First().ToString();
|
WgPublicKey = profileItem.GetProtocolExtra().WgPublicKey?.TrimEx(),
|
||||||
}
|
WgPresharedKey = profileItem.GetProtocolExtra().WgPresharedKey?.TrimEx(),
|
||||||
|
WgInterfaceAddress = profileItem.GetProtocolExtra().WgInterfaceAddress?.TrimEx(),
|
||||||
|
WgReserved = profileItem.GetProtocolExtra().WgReserved?.TrimEx(),
|
||||||
|
WgMtu = profileItem.GetProtocolExtra().WgMtu is null or <= 0 ? Global.TunMtus.First() : profileItem.GetProtocolExtra().WgMtu,
|
||||||
|
});
|
||||||
|
|
||||||
if (profileItem.Id.IsNullOrEmpty())
|
if (profileItem.Password.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -815,14 +819,13 @@ public static class ConfigHandler
|
||||||
profileItem.CoreType = ECoreType.sing_box;
|
profileItem.CoreType = ECoreType.sing_box;
|
||||||
|
|
||||||
profileItem.Address = profileItem.Address.TrimEx();
|
profileItem.Address = profileItem.Address.TrimEx();
|
||||||
profileItem.Id = profileItem.Id.TrimEx();
|
profileItem.Password = profileItem.Password.TrimEx();
|
||||||
profileItem.Security = profileItem.Security.TrimEx();
|
|
||||||
profileItem.Network = string.Empty;
|
profileItem.Network = string.Empty;
|
||||||
if (profileItem.StreamSecurity.IsNullOrEmpty())
|
if (profileItem.StreamSecurity.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
profileItem.StreamSecurity = Global.StreamSecurity;
|
profileItem.StreamSecurity = Global.StreamSecurity;
|
||||||
}
|
}
|
||||||
if (profileItem.Id.IsNullOrEmpty())
|
if (profileItem.Password.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -860,7 +863,7 @@ public static class ConfigHandler
|
||||||
Remarks = t.Remarks,
|
Remarks = t.Remarks,
|
||||||
Address = t.Address,
|
Address = t.Address,
|
||||||
Port = t.Port,
|
Port = t.Port,
|
||||||
Security = t.Security,
|
//Security = t.Security,
|
||||||
Network = t.Network,
|
Network = t.Network,
|
||||||
StreamSecurity = t.StreamSecurity,
|
StreamSecurity = t.StreamSecurity,
|
||||||
Delay = t33?.Delay ?? 0,
|
Delay = t33?.Delay ?? 0,
|
||||||
|
|
@ -959,26 +962,25 @@ public static class ConfigHandler
|
||||||
profileItem.ConfigType = EConfigType.VLESS;
|
profileItem.ConfigType = EConfigType.VLESS;
|
||||||
|
|
||||||
profileItem.Address = profileItem.Address.TrimEx();
|
profileItem.Address = profileItem.Address.TrimEx();
|
||||||
profileItem.Id = profileItem.Id.TrimEx();
|
profileItem.Password = profileItem.Password.TrimEx();
|
||||||
profileItem.Security = profileItem.Security.TrimEx();
|
|
||||||
profileItem.Network = profileItem.Network.TrimEx();
|
profileItem.Network = profileItem.Network.TrimEx();
|
||||||
profileItem.HeaderType = profileItem.HeaderType.TrimEx();
|
profileItem.HeaderType = profileItem.HeaderType.TrimEx();
|
||||||
profileItem.RequestHost = profileItem.RequestHost.TrimEx();
|
profileItem.RequestHost = profileItem.RequestHost.TrimEx();
|
||||||
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 vlessEncryption = profileItem.GetProtocolExtra().VlessEncryption?.TrimEx();
|
||||||
|
var flow = profileItem.GetProtocolExtra().Flow?.TrimEx() ?? string.Empty;
|
||||||
|
profileItem.SetProtocolExtra(profileItem.GetProtocolExtra() with
|
||||||
{
|
{
|
||||||
profileItem.Flow = Global.Flows.First();
|
VlessEncryption = vlessEncryption.IsNullOrEmpty() ? Global.None : vlessEncryption,
|
||||||
}
|
Flow = Global.Flows.Contains(flow) ? flow : Global.Flows.First(),
|
||||||
if (profileItem.Id.IsNullOrEmpty())
|
});
|
||||||
|
|
||||||
|
if (profileItem.Password.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (profileItem.Security.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
profileItem.Security = Global.None;
|
|
||||||
}
|
|
||||||
|
|
||||||
await AddServerCommon(config, profileItem, toFile);
|
await AddServerCommon(config, profileItem, toFile);
|
||||||
|
|
||||||
|
|
@ -1033,7 +1035,7 @@ public static class ConfigHandler
|
||||||
/// <returns>0 if successful</returns>
|
/// <returns>0 if successful</returns>
|
||||||
public static async Task<int> AddServerCommon(Config config, ProfileItem profileItem, bool toFile = true)
|
public static async Task<int> AddServerCommon(Config config, ProfileItem profileItem, bool toFile = true)
|
||||||
{
|
{
|
||||||
profileItem.ConfigVersion = 2;
|
profileItem.ConfigVersion = 3;
|
||||||
|
|
||||||
if (profileItem.StreamSecurity.IsNotEmpty())
|
if (profileItem.StreamSecurity.IsNotEmpty())
|
||||||
{
|
{
|
||||||
|
|
@ -1077,42 +1079,12 @@ public static class ConfigHandler
|
||||||
|
|
||||||
if (toFile)
|
if (toFile)
|
||||||
{
|
{
|
||||||
|
profileItem.SetProtocolExtra();
|
||||||
await SQLiteHelper.Instance.ReplaceAsync(profileItem);
|
await SQLiteHelper.Instance.ReplaceAsync(profileItem);
|
||||||
}
|
}
|
||||||
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,17 +1100,23 @@ public static class ConfigHandler
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var oProtocolExtra = o.GetProtocolExtra();
|
||||||
|
var nProtocolExtra = n.GetProtocolExtra();
|
||||||
|
|
||||||
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
|
||||||
&& AreEqual(o.Id, n.Id)
|
&& AreEqual(o.Password, n.Password)
|
||||||
&& AreEqual(o.Security, n.Security)
|
&& AreEqual(oProtocolExtra.VlessEncryption, nProtocolExtra.VlessEncryption)
|
||||||
|
&& AreEqual(oProtocolExtra.SsMethod, nProtocolExtra.SsMethod)
|
||||||
|
&& AreEqual(oProtocolExtra.VmessSecurity, nProtocolExtra.VmessSecurity)
|
||||||
&& AreEqual(o.Network, n.Network)
|
&& AreEqual(o.Network, n.Network)
|
||||||
&& AreEqual(o.HeaderType, n.HeaderType)
|
&& AreEqual(o.HeaderType, n.HeaderType)
|
||||||
&& 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(oProtocolExtra.Flow, nProtocolExtra.Flow)
|
||||||
|
&& AreEqual(oProtocolExtra.SalamanderPass, nProtocolExtra.SalamanderPass)
|
||||||
&& 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 +1177,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 +1211,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.SetProtocolExtra(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 +1238,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);
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ public class AnytlsFmt : BaseFmt
|
||||||
Port = parsedUrl.Port,
|
Port = parsedUrl.Port,
|
||||||
};
|
};
|
||||||
var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo);
|
var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo);
|
||||||
item.Id = rawUserInfo;
|
item.Password = rawUserInfo;
|
||||||
|
|
||||||
var query = Utils.ParseQueryString(parsedUrl.Query);
|
var query = Utils.ParseQueryString(parsedUrl.Query);
|
||||||
ResolveUriQuery(query, ref item);
|
ResolveUriQuery(query, ref item);
|
||||||
|
|
@ -39,7 +39,7 @@ public class AnytlsFmt : BaseFmt
|
||||||
{
|
{
|
||||||
remark = "#" + Utils.UrlEncode(item.Remarks);
|
remark = "#" + Utils.UrlEncode(item.Remarks);
|
||||||
}
|
}
|
||||||
var pw = item.Id;
|
var pw = item.Password;
|
||||||
var dicQuery = new Dictionary<string, string>();
|
var dicQuery = new Dictionary<string, string>();
|
||||||
ToUriQuery(item, Global.None, ref dicQuery);
|
ToUriQuery(item, Global.None, ref dicQuery);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,11 +21,6 @@ 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())
|
|
||||||
{
|
|
||||||
dicQuery.Add("flow", item.Flow);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item.StreamSecurity.IsNotEmpty())
|
if (item.StreamSecurity.IsNotEmpty())
|
||||||
{
|
{
|
||||||
dicQuery.Add("security", item.StreamSecurity);
|
dicQuery.Add("security", item.StreamSecurity);
|
||||||
|
|
@ -208,7 +203,6 @@ 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");
|
|
||||||
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");
|
||||||
|
|
|
||||||
|
|
@ -19,16 +19,19 @@ public class Hysteria2Fmt : BaseFmt
|
||||||
item.Address = url.IdnHost;
|
item.Address = url.IdnHost;
|
||||||
item.Port = url.Port;
|
item.Port = url.Port;
|
||||||
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
||||||
item.Id = Utils.UrlDecode(url.UserInfo);
|
item.Password = Utils.UrlDecode(url.UserInfo);
|
||||||
|
|
||||||
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.Ports = GetQueryDecoded(query, "mport");
|
|
||||||
if (item.CertSha.IsNullOrEmpty())
|
if (item.CertSha.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
item.CertSha = GetQueryDecoded(query, "pinSHA256");
|
item.CertSha = GetQueryDecoded(query, "pinSHA256");
|
||||||
}
|
}
|
||||||
|
item.SetProtocolExtra(item.GetProtocolExtra() with
|
||||||
|
{
|
||||||
|
Ports = GetQueryDecoded(query, "mport"),
|
||||||
|
SalamanderPass = GetQueryDecoded(query, "obfs-password"),
|
||||||
|
});
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
@ -49,15 +52,16 @@ public class Hysteria2Fmt : BaseFmt
|
||||||
}
|
}
|
||||||
var dicQuery = new Dictionary<string, string>();
|
var dicQuery = new Dictionary<string, string>();
|
||||||
ToUriQueryLite(item, ref dicQuery);
|
ToUriQueryLite(item, ref dicQuery);
|
||||||
|
var protocolExtraItem = item.GetProtocolExtra();
|
||||||
|
|
||||||
if (item.Path.IsNotEmpty())
|
if (!protocolExtraItem.SalamanderPass.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
dicQuery.Add("obfs", "salamander");
|
dicQuery.Add("obfs", "salamander");
|
||||||
dicQuery.Add("obfs-password", Utils.UrlEncode(item.Path));
|
dicQuery.Add("obfs-password", Utils.UrlEncode(protocolExtraItem.SalamanderPass));
|
||||||
}
|
}
|
||||||
if (item.Ports.IsNotEmpty())
|
if (!protocolExtraItem.Ports.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
dicQuery.Add("mport", Utils.UrlEncode(item.Ports.Replace(':', '-')));
|
dicQuery.Add("mport", Utils.UrlEncode(protocolExtraItem.Ports.Replace(':', '-')));
|
||||||
}
|
}
|
||||||
if (!item.CertSha.IsNullOrEmpty())
|
if (!item.CertSha.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
|
|
@ -70,7 +74,7 @@ public class Hysteria2Fmt : BaseFmt
|
||||||
dicQuery.Add("pinSHA256", Utils.UrlEncode(sha));
|
dicQuery.Add("pinSHA256", Utils.UrlEncode(sha));
|
||||||
}
|
}
|
||||||
|
|
||||||
return ToUri(EConfigType.Hysteria2, item.Address, item.Port, item.Id, dicQuery, remark);
|
return ToUri(EConfigType.Hysteria2, item.Address, item.Port, item.Password, dicQuery, remark);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ProfileItem? ResolveFull2(string strData, string? subRemarks)
|
public static ProfileItem? ResolveFull2(string strData, string? subRemarks)
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,8 @@ public class ShadowsocksFmt : BaseFmt
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (item.Address.Length == 0 || item.Port == 0 || item.Security.Length == 0 || item.Id.Length == 0)
|
|
||||||
|
if (item.Address.Length == 0 || item.Port == 0 || item.GetProtocolExtra().SsMethod.IsNullOrEmpty() || item.Password.Length == 0)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -40,7 +41,7 @@ public class ShadowsocksFmt : BaseFmt
|
||||||
// item.port);
|
// item.port);
|
||||||
//url = Utile.Base64Encode(url);
|
//url = Utile.Base64Encode(url);
|
||||||
//new Sip002
|
//new Sip002
|
||||||
var pw = Utils.Base64Encode($"{item.Security}:{item.Id}", true);
|
var pw = Utils.Base64Encode($"{item.GetProtocolExtra().SsMethod}:{item.Password}", true);
|
||||||
|
|
||||||
// plugin
|
// plugin
|
||||||
var plugin = string.Empty;
|
var plugin = string.Empty;
|
||||||
|
|
@ -136,8 +137,8 @@ public class ShadowsocksFmt : BaseFmt
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
item.Security = details.Groups["method"].Value;
|
item.SetProtocolExtra(item.GetProtocolExtra() with { SsMethod = details.Groups["method"].Value });
|
||||||
item.Id = details.Groups["password"].Value;
|
item.Password = details.Groups["password"].Value;
|
||||||
item.Address = details.Groups["hostname"].Value;
|
item.Address = details.Groups["hostname"].Value;
|
||||||
item.Port = details.Groups["port"].Value.ToInt();
|
item.Port = details.Groups["port"].Value.ToInt();
|
||||||
return item;
|
return item;
|
||||||
|
|
@ -166,8 +167,8 @@ public class ShadowsocksFmt : BaseFmt
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
item.Security = userInfoParts.First();
|
item.SetProtocolExtra(item.GetProtocolExtra() with { SsMethod = userInfoParts.First() });
|
||||||
item.Id = Utils.UrlDecode(userInfoParts.Last());
|
item.Password = Utils.UrlDecode(userInfoParts.Last());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -178,8 +179,8 @@ public class ShadowsocksFmt : BaseFmt
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
item.Security = userInfoParts.First();
|
item.SetProtocolExtra(item.GetProtocolExtra() with { SsMethod = userInfoParts.First() });
|
||||||
item.Id = userInfoParts.Last();
|
item.Password = userInfoParts.Last();
|
||||||
}
|
}
|
||||||
|
|
||||||
var queryParameters = Utils.ParseQueryString(parsedUrl.Query);
|
var queryParameters = Utils.ParseQueryString(parsedUrl.Query);
|
||||||
|
|
@ -275,7 +276,6 @@ public class ShadowsocksFmt : BaseFmt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -300,11 +300,11 @@ public class ShadowsocksFmt : BaseFmt
|
||||||
var ssItem = new ProfileItem()
|
var ssItem = new ProfileItem()
|
||||||
{
|
{
|
||||||
Remarks = it.remarks,
|
Remarks = it.remarks,
|
||||||
Security = it.method,
|
Password = it.password,
|
||||||
Id = it.password,
|
|
||||||
Address = it.server,
|
Address = it.server,
|
||||||
Port = it.server_port.ToInt()
|
Port = it.server_port.ToInt()
|
||||||
};
|
};
|
||||||
|
ssItem.SetProtocolExtra(new ProtocolExtraItem() { SsMethod = it.method });
|
||||||
lst.Add(ssItem);
|
lst.Add(ssItem);
|
||||||
}
|
}
|
||||||
return lst;
|
return lst;
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ public class SocksFmt : BaseFmt
|
||||||
remark = "#" + Utils.UrlEncode(item.Remarks);
|
remark = "#" + Utils.UrlEncode(item.Remarks);
|
||||||
}
|
}
|
||||||
//new
|
//new
|
||||||
var pw = Utils.Base64Encode($"{item.Security}:{item.Id}", true);
|
var pw = Utils.Base64Encode($"{item.Username}:{item.Password}", true);
|
||||||
return ToUri(EConfigType.SOCKS, item.Address, item.Port, pw, null, remark);
|
return ToUri(EConfigType.SOCKS, item.Address, item.Port, pw, null, remark);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -78,9 +78,8 @@ public class SocksFmt : BaseFmt
|
||||||
}
|
}
|
||||||
item.Address = arr1[1][..indexPort];
|
item.Address = arr1[1][..indexPort];
|
||||||
item.Port = arr1[1][(indexPort + 1)..].ToInt();
|
item.Port = arr1[1][(indexPort + 1)..].ToInt();
|
||||||
item.Security = arr21.First();
|
item.Username = arr21.First();
|
||||||
item.Id = arr21[1];
|
item.Password = arr21[1];
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -98,15 +97,14 @@ public class SocksFmt : BaseFmt
|
||||||
Address = parsedUrl.IdnHost,
|
Address = parsedUrl.IdnHost,
|
||||||
Port = parsedUrl.Port,
|
Port = parsedUrl.Port,
|
||||||
};
|
};
|
||||||
|
|
||||||
// parse base64 UserInfo
|
// parse base64 UserInfo
|
||||||
var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo);
|
var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo);
|
||||||
var userInfo = Utils.Base64Decode(rawUserInfo);
|
var userInfo = Utils.Base64Decode(rawUserInfo);
|
||||||
var userInfoParts = userInfo.Split([':'], 2);
|
var userInfoParts = userInfo.Split([':'], 2);
|
||||||
if (userInfoParts.Length == 2)
|
if (userInfoParts.Length == 2)
|
||||||
{
|
{
|
||||||
item.Security = userInfoParts.First();
|
item.Username = userInfoParts.First();
|
||||||
item.Id = userInfoParts[1];
|
item.Password = userInfoParts[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,10 @@ public class TrojanFmt : BaseFmt
|
||||||
item.Address = url.IdnHost;
|
item.Address = url.IdnHost;
|
||||||
item.Port = url.Port;
|
item.Port = url.Port;
|
||||||
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
||||||
item.Id = Utils.UrlDecode(url.UserInfo);
|
item.Password = Utils.UrlDecode(url.UserInfo);
|
||||||
|
|
||||||
var query = Utils.ParseQueryString(url.Query);
|
var query = Utils.ParseQueryString(url.Query);
|
||||||
|
item.SetProtocolExtra(item.GetProtocolExtra() with { Flow = GetQueryValue(query, "flow") });
|
||||||
ResolveUriQuery(query, ref item);
|
ResolveUriQuery(query, ref item);
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
|
|
@ -40,8 +41,12 @@ public class TrojanFmt : BaseFmt
|
||||||
remark = "#" + Utils.UrlEncode(item.Remarks);
|
remark = "#" + Utils.UrlEncode(item.Remarks);
|
||||||
}
|
}
|
||||||
var dicQuery = new Dictionary<string, string>();
|
var dicQuery = new Dictionary<string, string>();
|
||||||
|
if (!item.GetProtocolExtra().Flow.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
dicQuery.Add("flow", item.GetProtocolExtra().Flow);
|
||||||
|
}
|
||||||
ToUriQuery(item, null, ref dicQuery);
|
ToUriQuery(item, null, ref dicQuery);
|
||||||
|
|
||||||
return ToUri(EConfigType.Trojan, item.Address, item.Port, item.Id, dicQuery, remark);
|
return ToUri(EConfigType.Trojan, item.Address, item.Port, item.Password, dicQuery, remark);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,8 @@ public class TuicFmt : BaseFmt
|
||||||
var userInfoParts = rawUserInfo.Split(new[] { ':' }, 2);
|
var userInfoParts = rawUserInfo.Split(new[] { ':' }, 2);
|
||||||
if (userInfoParts.Length == 2)
|
if (userInfoParts.Length == 2)
|
||||||
{
|
{
|
||||||
item.Id = userInfoParts.First();
|
item.Username = userInfoParts.First();
|
||||||
item.Security = userInfoParts.Last();
|
item.Password = userInfoParts.Last();
|
||||||
}
|
}
|
||||||
|
|
||||||
var query = Utils.ParseQueryString(url.Query);
|
var query = Utils.ParseQueryString(url.Query);
|
||||||
|
|
@ -53,6 +53,6 @@ public class TuicFmt : BaseFmt
|
||||||
|
|
||||||
dicQuery.Add("congestion_control", item.HeaderType);
|
dicQuery.Add("congestion_control", item.HeaderType);
|
||||||
|
|
||||||
return ToUri(EConfigType.TUIC, item.Address, item.Port, $"{item.Id}:{item.Security}", dicQuery, remark);
|
return ToUri(EConfigType.TUIC, item.Address, item.Port, $"{item.Username ?? ""}:{item.Password}", dicQuery, remark);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ public class VLESSFmt : BaseFmt
|
||||||
ProfileItem item = new()
|
ProfileItem item = new()
|
||||||
{
|
{
|
||||||
ConfigType = EConfigType.VLESS,
|
ConfigType = EConfigType.VLESS,
|
||||||
Security = Global.None
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var url = Utils.TryUri(str);
|
var url = Utils.TryUri(str);
|
||||||
|
|
@ -21,10 +20,14 @@ public class VLESSFmt : BaseFmt
|
||||||
item.Address = url.IdnHost;
|
item.Address = url.IdnHost;
|
||||||
item.Port = url.Port;
|
item.Port = url.Port;
|
||||||
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
||||||
item.Id = Utils.UrlDecode(url.UserInfo);
|
item.Password = Utils.UrlDecode(url.UserInfo);
|
||||||
|
|
||||||
var query = Utils.ParseQueryString(url.Query);
|
var query = Utils.ParseQueryString(url.Query);
|
||||||
item.Security = GetQueryValue(query, "encryption", Global.None);
|
item.SetProtocolExtra(item.GetProtocolExtra() with
|
||||||
|
{
|
||||||
|
VlessEncryption = GetQueryValue(query, "encryption", Global.None),
|
||||||
|
Flow = GetQueryValue(query, "flow")
|
||||||
|
});
|
||||||
item.StreamSecurity = GetQueryValue(query, "security");
|
item.StreamSecurity = GetQueryValue(query, "security");
|
||||||
ResolveUriQuery(query, ref item);
|
ResolveUriQuery(query, ref item);
|
||||||
|
|
||||||
|
|
@ -44,16 +47,14 @@ public class VLESSFmt : BaseFmt
|
||||||
remark = "#" + Utils.UrlEncode(item.Remarks);
|
remark = "#" + Utils.UrlEncode(item.Remarks);
|
||||||
}
|
}
|
||||||
var dicQuery = new Dictionary<string, string>();
|
var dicQuery = new Dictionary<string, string>();
|
||||||
if (item.Security.IsNotEmpty())
|
dicQuery.Add("encryption",
|
||||||
|
!item.GetProtocolExtra().VlessEncryption.IsNullOrEmpty() ? item.GetProtocolExtra().VlessEncryption : Global.None);
|
||||||
|
if (!item.GetProtocolExtra().Flow.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
dicQuery.Add("encryption", item.Security);
|
dicQuery.Add("flow", item.GetProtocolExtra().Flow);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dicQuery.Add("encryption", Global.None);
|
|
||||||
}
|
}
|
||||||
ToUriQuery(item, Global.None, ref dicQuery);
|
ToUriQuery(item, Global.None, ref dicQuery);
|
||||||
|
|
||||||
return ToUri(EConfigType.VLESS, item.Address, item.Port, item.Id, dicQuery, remark);
|
return ToUri(EConfigType.VLESS, item.Address, item.Port, item.Password, dicQuery, remark);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,15 +23,16 @@ public class VmessFmt : BaseFmt
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var vmessQRCode = new VmessQRCode
|
var vmessQRCode = new VmessQRCode
|
||||||
{
|
{
|
||||||
v = item.ConfigVersion,
|
v = 2,
|
||||||
ps = item.Remarks.TrimEx(),
|
ps = item.Remarks.TrimEx(),
|
||||||
add = item.Address,
|
add = item.Address,
|
||||||
port = item.Port,
|
port = item.Port,
|
||||||
id = item.Id,
|
id = item.Password,
|
||||||
aid = item.AlterId,
|
aid = int.TryParse(item.GetProtocolExtra()?.AlterId, out var result) ? result : 0,
|
||||||
scy = item.Security,
|
scy = item.GetProtocolExtra().VmessSecurity ?? "",
|
||||||
net = item.Network,
|
net = item.Network,
|
||||||
type = item.HeaderType,
|
type = item.HeaderType,
|
||||||
host = item.RequestHost,
|
host = item.RequestHost,
|
||||||
|
|
@ -71,15 +72,16 @@ public class VmessFmt : BaseFmt
|
||||||
item.Network = Global.DefaultNetwork;
|
item.Network = Global.DefaultNetwork;
|
||||||
item.HeaderType = Global.None;
|
item.HeaderType = Global.None;
|
||||||
|
|
||||||
item.ConfigVersion = vmessQRCode.v;
|
//item.ConfigVersion = vmessQRCode.v;
|
||||||
item.Remarks = Utils.ToString(vmessQRCode.ps);
|
item.Remarks = Utils.ToString(vmessQRCode.ps);
|
||||||
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.Password = Utils.ToString(vmessQRCode.id);
|
||||||
item.AlterId = vmessQRCode.aid;
|
item.SetProtocolExtra(new ProtocolExtraItem
|
||||||
item.Security = Utils.ToString(vmessQRCode.scy);
|
{
|
||||||
|
AlterId = vmessQRCode.aid.ToString(),
|
||||||
item.Security = vmessQRCode.scy.IsNotEmpty() ? vmessQRCode.scy : Global.DefaultSecurity;
|
VmessSecurity = vmessQRCode.scy.IsNullOrEmpty() ? Global.DefaultSecurity : vmessQRCode.scy,
|
||||||
|
});
|
||||||
if (vmessQRCode.net.IsNotEmpty())
|
if (vmessQRCode.net.IsNotEmpty())
|
||||||
{
|
{
|
||||||
item.Network = vmessQRCode.net;
|
item.Network = vmessQRCode.net;
|
||||||
|
|
@ -105,7 +107,6 @@ public class VmessFmt : BaseFmt
|
||||||
var item = new ProfileItem
|
var item = new ProfileItem
|
||||||
{
|
{
|
||||||
ConfigType = EConfigType.VMess,
|
ConfigType = EConfigType.VMess,
|
||||||
Security = "auto"
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var url = Utils.TryUri(str);
|
var url = Utils.TryUri(str);
|
||||||
|
|
@ -117,7 +118,12 @@ public class VmessFmt : BaseFmt
|
||||||
item.Address = url.IdnHost;
|
item.Address = url.IdnHost;
|
||||||
item.Port = url.Port;
|
item.Port = url.Port;
|
||||||
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
||||||
item.Id = Utils.UrlDecode(url.UserInfo);
|
item.Password = Utils.UrlDecode(url.UserInfo);
|
||||||
|
|
||||||
|
item.SetProtocolExtra(new ProtocolExtraItem
|
||||||
|
{
|
||||||
|
VmessSecurity = "auto",
|
||||||
|
});
|
||||||
|
|
||||||
var query = Utils.ParseQueryString(url.Query);
|
var query = Utils.ParseQueryString(url.Query);
|
||||||
ResolveUriQuery(query, ref item);
|
ResolveUriQuery(query, ref item);
|
||||||
|
|
|
||||||
|
|
@ -20,14 +20,17 @@ public class WireguardFmt : BaseFmt
|
||||||
item.Address = url.IdnHost;
|
item.Address = url.IdnHost;
|
||||||
item.Port = url.Port;
|
item.Port = url.Port;
|
||||||
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
||||||
item.Id = Utils.UrlDecode(url.UserInfo);
|
item.Password = Utils.UrlDecode(url.UserInfo);
|
||||||
|
|
||||||
var query = Utils.ParseQueryString(url.Query);
|
var query = Utils.ParseQueryString(url.Query);
|
||||||
|
|
||||||
item.PublicKey = GetQueryDecoded(query, "publickey");
|
item.SetProtocolExtra(item.GetProtocolExtra() with
|
||||||
item.Path = GetQueryDecoded(query, "reserved");
|
{
|
||||||
item.RequestHost = GetQueryDecoded(query, "address");
|
WgPublicKey = GetQueryDecoded(query, "publickey"),
|
||||||
item.ShortId = GetQueryDecoded(query, "mtu");
|
WgReserved = GetQueryDecoded(query, "reserved"),
|
||||||
|
WgInterfaceAddress = GetQueryDecoded(query, "address"),
|
||||||
|
WgMtu = int.TryParse(GetQueryDecoded(query, "mtu"), out var mtuVal) ? mtuVal : 1280,
|
||||||
|
});
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
@ -46,22 +49,19 @@ public class WireguardFmt : BaseFmt
|
||||||
}
|
}
|
||||||
|
|
||||||
var dicQuery = new Dictionary<string, string>();
|
var dicQuery = new Dictionary<string, string>();
|
||||||
if (item.PublicKey.IsNotEmpty())
|
if (!item.GetProtocolExtra().WgPublicKey.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
dicQuery.Add("publickey", Utils.UrlEncode(item.PublicKey));
|
dicQuery.Add("publickey", Utils.UrlEncode(item.GetProtocolExtra().WgPublicKey));
|
||||||
}
|
}
|
||||||
if (item.Path.IsNotEmpty())
|
if (!item.GetProtocolExtra().WgReserved.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
dicQuery.Add("reserved", Utils.UrlEncode(item.Path));
|
dicQuery.Add("reserved", Utils.UrlEncode(item.GetProtocolExtra().WgReserved));
|
||||||
}
|
}
|
||||||
if (item.RequestHost.IsNotEmpty())
|
if (!item.GetProtocolExtra().WgInterfaceAddress.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
dicQuery.Add("address", Utils.UrlEncode(item.RequestHost));
|
dicQuery.Add("address", Utils.UrlEncode(item.GetProtocolExtra().WgInterfaceAddress));
|
||||||
}
|
}
|
||||||
if (item.ShortId.IsNotEmpty())
|
dicQuery.Add("mtu", Utils.UrlEncode(item.GetProtocolExtra().WgMtu > 0 ? item.GetProtocolExtra().WgMtu.ToString() : "1280"));
|
||||||
{
|
return ToUri(EConfigType.WireGuard, item.Address, item.Port, item.Password, dicQuery, remark);
|
||||||
dicQuery.Add("mtu", Utils.UrlEncode(item.ShortId));
|
|
||||||
}
|
|
||||||
return ToUri(EConfigType.WireGuard, item.Address, item.Port, item.Id, dicQuery, remark);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -128,23 +128,25 @@ public class ActionPrecheckManager
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var protocolExtra = item.GetProtocolExtra();
|
||||||
|
|
||||||
switch (item.ConfigType)
|
switch (item.ConfigType)
|
||||||
{
|
{
|
||||||
case EConfigType.VMess:
|
case EConfigType.VMess:
|
||||||
if (item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id))
|
if (item.Password.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Password))
|
||||||
{
|
{
|
||||||
errors.Add(string.Format(ResUI.InvalidProperty, "Id"));
|
errors.Add(string.Format(ResUI.InvalidProperty, "Password"));
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.VLESS:
|
case EConfigType.VLESS:
|
||||||
if (item.Id.IsNullOrEmpty() || (!Utils.IsGuidByParse(item.Id) && item.Id.Length > 30))
|
if (item.Password.IsNullOrEmpty() || (!Utils.IsGuidByParse(item.Password) && item.Password.Length > 30))
|
||||||
{
|
{
|
||||||
errors.Add(string.Format(ResUI.InvalidProperty, "Id"));
|
errors.Add(string.Format(ResUI.InvalidProperty, "Password"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Global.Flows.Contains(item.Flow))
|
if (!Global.Flows.Contains(protocolExtra.Flow ?? string.Empty))
|
||||||
{
|
{
|
||||||
errors.Add(string.Format(ResUI.InvalidProperty, "Flow"));
|
errors.Add(string.Format(ResUI.InvalidProperty, "Flow"));
|
||||||
}
|
}
|
||||||
|
|
@ -152,14 +154,14 @@ public class ActionPrecheckManager
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.Shadowsocks:
|
case EConfigType.Shadowsocks:
|
||||||
if (item.Id.IsNullOrEmpty())
|
if (item.Password.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
errors.Add(string.Format(ResUI.InvalidProperty, "Id"));
|
errors.Add(string.Format(ResUI.InvalidProperty, "Password"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(item.Security) || !Global.SsSecuritiesInSingbox.Contains(item.Security))
|
if (string.IsNullOrEmpty(protocolExtra.SsMethod) || !Global.SsSecuritiesInSingbox.Contains(protocolExtra.SsMethod))
|
||||||
{
|
{
|
||||||
errors.Add(string.Format(ResUI.InvalidProperty, "Security"));
|
errors.Add(string.Format(ResUI.InvalidProperty, "SsMethod"));
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
@ -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.GetProtocolExtra());
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,9 @@ 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>();
|
||||||
|
#pragma warning disable CS0618
|
||||||
SQLiteHelper.Instance.CreateTable<ProfileGroupItem>();
|
SQLiteHelper.Instance.CreateTable<ProfileGroupItem>();
|
||||||
|
#pragma warning restore CS0618
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -94,6 +96,11 @@ public sealed class AppManager
|
||||||
_ = StatePort;
|
_ = StatePort;
|
||||||
_ = StatePort2;
|
_ = StatePort2;
|
||||||
|
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await MigrateProfileExtra();
|
||||||
|
}).Wait();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -225,15 +232,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 +262,119 @@ 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()
|
||||||
|
{
|
||||||
|
#pragma warning disable CS0618
|
||||||
|
var list = await SQLiteHelper.Instance.TableAsync<ProfileGroupItem>().ToListAsync();
|
||||||
|
var groupItems = new ConcurrentDictionary<string, ProfileGroupItem>(list.Where(t => !string.IsNullOrEmpty(t.IndexId)).ToDictionary(t => t.IndexId!));
|
||||||
|
|
||||||
|
const int pageSize = 500;
|
||||||
|
var offset = 0;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var sql = $"SELECT * FROM ProfileItem WHERE ConfigVersion < 3 LIMIT {pageSize} OFFSET {offset}";
|
||||||
|
var batch = await SQLiteHelper.Instance.QueryAsync<ProfileItem>(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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
offset += batch.Count - batchSuccessCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
//await ProfileGroupItemManager.Instance.ClearAll();
|
||||||
|
#pragma warning restore CS0618
|
||||||
|
}
|
||||||
|
|
||||||
#endregion SqliteHelper
|
#endregion SqliteHelper
|
||||||
|
|
||||||
#region Core Type
|
#region Core Type
|
||||||
|
|
|
||||||
180
v2rayN/ServiceLib/Manager/GroupProfileManager.cs
Normal file
180
v2rayN/ServiceLib/Manager/GroupProfileManager.cs
Normal file
|
|
@ -0,0 +1,180 @@
|
||||||
|
namespace ServiceLib.Manager;
|
||||||
|
|
||||||
|
public class GroupProfileManager
|
||||||
|
{
|
||||||
|
public static async Task<bool> HasCycle(ProfileItem item)
|
||||||
|
{
|
||||||
|
return await HasCycle(item.IndexId, item.GetProtocolExtra());
|
||||||
|
}
|
||||||
|
|
||||||
|
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?.GetProtocolExtra(), visited, stack))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
stack.Remove(indexId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<(List<ProfileItem> Items, ProtocolExtraItem? Extra)> GetChildProfileItems(ProfileItem profileItem)
|
||||||
|
{
|
||||||
|
var protocolExtra = profileItem?.GetProtocolExtra();
|
||||||
|
return (await GetChildProfileItemsByProtocolExtra(protocolExtra), protocolExtra);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<List<ProfileItem>> GetChildProfileItemsByProtocolExtra(ProtocolExtraItem? protocolExtra)
|
||||||
|
{
|
||||||
|
if (protocolExtra == null)
|
||||||
|
{
|
||||||
|
return new();
|
||||||
|
}
|
||||||
|
var items = await GetSelectedChildProfileItems(protocolExtra);
|
||||||
|
var subItems = await GetSubChildProfileItems(protocolExtra);
|
||||||
|
items.AddRange(subItems);
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<List<ProfileItem>> GetSelectedChildProfileItems(ProtocolExtraItem? extra)
|
||||||
|
{
|
||||||
|
if (extra == null || extra.ChildItems.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
return new();
|
||||||
|
}
|
||||||
|
var childProfiles = (await Task.WhenAll(
|
||||||
|
(Utils.String2List(extra.ChildItems) ?? new())
|
||||||
|
.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.SubChildItems.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() ?? new();
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,388 +0,0 @@
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read-only getters: do not create or mark dirty
|
|
||||||
public bool TryGet(string indexId, out ProfileGroupItem? item)
|
|
||||||
{
|
|
||||||
item = null;
|
|
||||||
if (string.IsNullOrWhiteSpace(indexId))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 )");
|
|
||||||
|
|
||||||
var list = await SQLiteHelper.Instance.TableAsync<ProfileGroupItem>().ToListAsync();
|
|
||||||
_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;
|
namespace ServiceLib.Models;
|
||||||
|
|
||||||
|
[Obsolete("Use ProtocolExtraItem instead.")]
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class ProfileGroupItem
|
public class ProfileGroupItem
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,17 @@ namespace ServiceLib.Models;
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class ProfileItem : ReactiveObject
|
public class ProfileItem : ReactiveObject
|
||||||
{
|
{
|
||||||
|
private ProtocolExtraItem? _protocolExtraCache;
|
||||||
|
|
||||||
public ProfileItem()
|
public ProfileItem()
|
||||||
{
|
{
|
||||||
IndexId = string.Empty;
|
IndexId = string.Empty;
|
||||||
ConfigType = EConfigType.VMess;
|
ConfigType = EConfigType.VMess;
|
||||||
ConfigVersion = 2;
|
ConfigVersion = 3;
|
||||||
Address = string.Empty;
|
Address = string.Empty;
|
||||||
Port = 0;
|
Port = 0;
|
||||||
Id = string.Empty;
|
Password = string.Empty;
|
||||||
AlterId = 0;
|
Username = string.Empty;
|
||||||
Security = string.Empty;
|
|
||||||
Network = string.Empty;
|
Network = string.Empty;
|
||||||
Remarks = string.Empty;
|
Remarks = string.Empty;
|
||||||
HeaderType = string.Empty;
|
HeaderType = string.Empty;
|
||||||
|
|
@ -21,7 +22,6 @@ public class ProfileItem : ReactiveObject
|
||||||
StreamSecurity = string.Empty;
|
StreamSecurity = string.Empty;
|
||||||
AllowInsecure = string.Empty;
|
AllowInsecure = string.Empty;
|
||||||
Subid = string.Empty;
|
Subid = string.Empty;
|
||||||
Flow = string.Empty;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#region function
|
#region function
|
||||||
|
|
@ -81,7 +81,7 @@ public class ProfileItem : ReactiveObject
|
||||||
switch (ConfigType)
|
switch (ConfigType)
|
||||||
{
|
{
|
||||||
case EConfigType.VMess:
|
case EConfigType.VMess:
|
||||||
if (Id.IsNullOrEmpty() || !Utils.IsGuidByParse(Id))
|
if (Password.IsNullOrEmpty() || !Utils.IsGuidByParse(Password))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -89,12 +89,12 @@ public class ProfileItem : ReactiveObject
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.VLESS:
|
case EConfigType.VLESS:
|
||||||
if (Id.IsNullOrEmpty() || (!Utils.IsGuidByParse(Id) && Id.Length > 30))
|
if (Password.IsNullOrEmpty() || (!Utils.IsGuidByParse(Password) && Password.Length > 30))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Global.Flows.Contains(Flow))
|
if (!Global.Flows.Contains(GetProtocolExtra().Flow ?? string.Empty))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -102,12 +102,13 @@ public class ProfileItem : ReactiveObject
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.Shadowsocks:
|
case EConfigType.Shadowsocks:
|
||||||
if (Id.IsNullOrEmpty())
|
if (Password.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(Security) || !Global.SsSecuritiesInSingbox.Contains(Security))
|
if (string.IsNullOrEmpty(GetProtocolExtra().SsMethod)
|
||||||
|
|| !Global.SsSecuritiesInSingbox.Contains(GetProtocolExtra().SsMethod))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -125,6 +126,22 @@ public class ProfileItem : ReactiveObject
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetProtocolExtra(ProtocolExtraItem extraItem)
|
||||||
|
{
|
||||||
|
_protocolExtraCache = extraItem;
|
||||||
|
ProtoExtra = JsonUtils.Serialize(extraItem, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetProtocolExtra()
|
||||||
|
{
|
||||||
|
ProtoExtra = JsonUtils.Serialize(_protocolExtraCache, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProtocolExtraItem GetProtocolExtra()
|
||||||
|
{
|
||||||
|
return _protocolExtraCache ??= JsonUtils.Deserialize<ProtocolExtraItem>(ProtoExtra) ?? new ProtocolExtraItem();
|
||||||
|
}
|
||||||
|
|
||||||
#endregion function
|
#endregion function
|
||||||
|
|
||||||
[PrimaryKey]
|
[PrimaryKey]
|
||||||
|
|
@ -134,10 +151,8 @@ 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 Password { get; set; }
|
||||||
public string Id { get; set; }
|
public string Username { get; set; }
|
||||||
public int AlterId { 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; }
|
||||||
public string HeaderType { get; set; }
|
public string HeaderType { get; set; }
|
||||||
|
|
@ -147,7 +162,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 +178,21 @@ 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 ProtoExtra { get; set; }
|
||||||
|
|
||||||
|
[Obsolete("Use ProtocolExtraItem.Ports instead.")]
|
||||||
|
public string Ports { get; set; }
|
||||||
|
|
||||||
|
[Obsolete("Use ProtocolExtraItem.AlterId instead.")]
|
||||||
|
public int AlterId { get; set; }
|
||||||
|
|
||||||
|
[Obsolete("Use ProtocolExtraItem.Flow instead.")]
|
||||||
|
public string Flow { get; set; }
|
||||||
|
|
||||||
|
[Obsolete("Use ProfileItem.Password instead.")]
|
||||||
|
public string Id { get; set; }
|
||||||
|
|
||||||
|
[Obsolete("Use ProtocolExtraItem.xxx instead.")]
|
||||||
|
public string Security { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
38
v2rayN/ServiceLib/Models/ProtocolExtraItem.cs
Normal file
38
v2rayN/ServiceLib/Models/ProtocolExtraItem.cs
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
namespace ServiceLib.Models;
|
||||||
|
|
||||||
|
public record ProtocolExtraItem
|
||||||
|
{
|
||||||
|
// vmess
|
||||||
|
public string? AlterId { get; init; }
|
||||||
|
public string? VmessSecurity { get; init; }
|
||||||
|
|
||||||
|
// vless
|
||||||
|
public string? Flow { get; init; }
|
||||||
|
public string? VlessEncryption { get; init; }
|
||||||
|
//public string? VisionSeed { get; init; }
|
||||||
|
|
||||||
|
// shadowsocks
|
||||||
|
//public string? PluginArgs { get; init; }
|
||||||
|
public string? SsMethod { get; init; }
|
||||||
|
|
||||||
|
// wireguard
|
||||||
|
public string? WgPublicKey { get; init; }
|
||||||
|
public string? WgPresharedKey { get; init; }
|
||||||
|
public string? WgInterfaceAddress { get; init; }
|
||||||
|
public string? WgReserved { get; init; }
|
||||||
|
public int? WgMtu { get; init; }
|
||||||
|
|
||||||
|
// hysteria2
|
||||||
|
public string? SalamanderPass { get; init; }
|
||||||
|
public int? UpMbps { get; init; }
|
||||||
|
public int? DownMbps { get; init; }
|
||||||
|
public string? Ports { get; init; }
|
||||||
|
public string? HopInterval { get; init; }
|
||||||
|
|
||||||
|
// group profile
|
||||||
|
public string? GroupType { get; init; }
|
||||||
|
public string? ChildItems { get; init; }
|
||||||
|
public string? SubChildItems { get; init; }
|
||||||
|
public string? Filter { get; init; }
|
||||||
|
public EMultipleLoad? MultipleLoad { get; init; }
|
||||||
|
}
|
||||||
|
|
@ -473,7 +473,7 @@ public class HysteriaSettings4Ray
|
||||||
public class HysteriaUdpHop4Ray
|
public class HysteriaUdpHop4Ray
|
||||||
{
|
{
|
||||||
public string? ports { get; set; }
|
public string? ports { get; set; }
|
||||||
public int? interval { get; set; }
|
public string? interval { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FinalMask4Ray
|
public class FinalMask4Ray
|
||||||
|
|
|
||||||
9
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
9
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
|
|
@ -2997,6 +2997,15 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Port hopping interval 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbHopInt7 {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbHopInt7", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 UUID(id) 的本地化字符串。
|
/// 查找类似 UUID(id) 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -1665,4 +1665,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||||
<data name="TbRemoteResolveStrategyTips" xml:space="preserve">
|
<data name="TbRemoteResolveStrategyTips" xml:space="preserve">
|
||||||
<value>If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used.</value>
|
<value>If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TbHopInt7" xml:space="preserve">
|
||||||
|
<value>Port hopping interval</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -1662,4 +1662,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||||
<data name="TbRemoteResolveStrategyTips" xml:space="preserve">
|
<data name="TbRemoteResolveStrategyTips" xml:space="preserve">
|
||||||
<value>If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used.</value>
|
<value>If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TbHopInt7" xml:space="preserve">
|
||||||
|
<value>Port hopping interval</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -1665,4 +1665,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||||
<data name="TbRemoteResolveStrategyTips" xml:space="preserve">
|
<data name="TbRemoteResolveStrategyTips" xml:space="preserve">
|
||||||
<value>If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used.</value>
|
<value>If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TbHopInt7" xml:space="preserve">
|
||||||
|
<value>Port hopping interval</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -1665,4 +1665,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||||
<data name="TbRemoteResolveStrategyTips" xml:space="preserve">
|
<data name="TbRemoteResolveStrategyTips" xml:space="preserve">
|
||||||
<value>If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used.</value>
|
<value>If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TbHopInt7" xml:space="preserve">
|
||||||
|
<value>Port hopping interval</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -1665,4 +1665,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||||
<data name="TbRemoteResolveStrategyTips" xml:space="preserve">
|
<data name="TbRemoteResolveStrategyTips" xml:space="preserve">
|
||||||
<value>If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used.</value>
|
<value>If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TbHopInt7" xml:space="preserve">
|
||||||
|
<value>Port hopping interval</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -1662,4 +1662,7 @@
|
||||||
<data name="TbRemoteResolveStrategyTips" xml:space="preserve">
|
<data name="TbRemoteResolveStrategyTips" xml:space="preserve">
|
||||||
<value>当未选择或 "AsIs" 时,由远程服务器端 DNS 解析;否则,使用内部 DNS 模块解析。</value>
|
<value>当未选择或 "AsIs" 时,由远程服务器端 DNS 解析;否则,使用内部 DNS 模块解析。</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TbHopInt7" xml:space="preserve">
|
||||||
|
<value>端口跳跃间隔</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -1662,4 +1662,7 @@
|
||||||
<data name="TbRemoteResolveStrategyTips" xml:space="preserve">
|
<data name="TbRemoteResolveStrategyTips" xml:space="preserve">
|
||||||
<value>If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used.</value>
|
<value>If unset or "AsIs", DNS resolution is performed by the remote server's DNS; otherwise, the internal DNS module is used.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TbHopInt7" xml:space="preserve">
|
||||||
|
<value>Port hopping interval</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -242,7 +242,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 protocolExtra = node.GetProtocolExtra();
|
||||||
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];
|
||||||
|
|
@ -14,11 +15,11 @@ public partial class CoreConfigSingboxService
|
||||||
{
|
{
|
||||||
case EConfigType.VMess:
|
case EConfigType.VMess:
|
||||||
{
|
{
|
||||||
outbound.uuid = node.Id;
|
outbound.uuid = node.Password;
|
||||||
outbound.alter_id = node.AlterId;
|
outbound.alter_id = int.TryParse(protocolExtra.AlterId, out var result) ? result : 0;
|
||||||
if (Global.VmessSecurities.Contains(node.Security))
|
if (Global.VmessSecurities.Contains(protocolExtra.VmessSecurity))
|
||||||
{
|
{
|
||||||
outbound.security = node.Security;
|
outbound.security = protocolExtra.VmessSecurity;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -31,8 +32,9 @@ public partial class CoreConfigSingboxService
|
||||||
}
|
}
|
||||||
case EConfigType.Shadowsocks:
|
case EConfigType.Shadowsocks:
|
||||||
{
|
{
|
||||||
outbound.method = AppManager.Instance.GetShadowsocksSecurities(node).Contains(node.Security) ? node.Security : Global.None;
|
outbound.method = AppManager.Instance.GetShadowsocksSecurities(node).Contains(protocolExtra.SsMethod)
|
||||||
outbound.password = node.Id;
|
? protocolExtra.SsMethod : Global.None;
|
||||||
|
outbound.password = node.Password;
|
||||||
|
|
||||||
if (node.Network == nameof(ETransport.tcp) && node.HeaderType == Global.TcpHeaderHttp)
|
if (node.Network == nameof(ETransport.tcp) && node.HeaderType == Global.TcpHeaderHttp)
|
||||||
{
|
{
|
||||||
|
|
@ -88,37 +90,37 @@ public partial class CoreConfigSingboxService
|
||||||
case EConfigType.SOCKS:
|
case EConfigType.SOCKS:
|
||||||
{
|
{
|
||||||
outbound.version = "5";
|
outbound.version = "5";
|
||||||
if (node.Security.IsNotEmpty()
|
if (node.Username.IsNotEmpty()
|
||||||
&& node.Id.IsNotEmpty())
|
&& node.Password.IsNotEmpty())
|
||||||
{
|
{
|
||||||
outbound.username = node.Security;
|
outbound.username = node.Username;
|
||||||
outbound.password = node.Id;
|
outbound.password = node.Password;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EConfigType.HTTP:
|
case EConfigType.HTTP:
|
||||||
{
|
{
|
||||||
if (node.Security.IsNotEmpty()
|
if (node.Username.IsNotEmpty()
|
||||||
&& node.Id.IsNotEmpty())
|
&& node.Password.IsNotEmpty())
|
||||||
{
|
{
|
||||||
outbound.username = node.Security;
|
outbound.username = node.Username;
|
||||||
outbound.password = node.Id;
|
outbound.password = node.Password;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EConfigType.VLESS:
|
case EConfigType.VLESS:
|
||||||
{
|
{
|
||||||
outbound.uuid = node.Id;
|
outbound.uuid = node.Password;
|
||||||
|
|
||||||
outbound.packet_encoding = "xudp";
|
outbound.packet_encoding = "xudp";
|
||||||
|
|
||||||
if (node.Flow.IsNullOrEmpty())
|
if (!protocolExtra.Flow.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
await GenOutboundMux(node, outbound);
|
outbound.flow = protocolExtra.Flow;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
outbound.flow = node.Flow;
|
await GenOutboundMux(node, outbound);
|
||||||
}
|
}
|
||||||
|
|
||||||
await GenOutboundTransport(node, outbound);
|
await GenOutboundTransport(node, outbound);
|
||||||
|
|
@ -126,7 +128,7 @@ public partial class CoreConfigSingboxService
|
||||||
}
|
}
|
||||||
case EConfigType.Trojan:
|
case EConfigType.Trojan:
|
||||||
{
|
{
|
||||||
outbound.password = node.Id;
|
outbound.password = node.Password;
|
||||||
|
|
||||||
await GenOutboundMux(node, outbound);
|
await GenOutboundMux(node, outbound);
|
||||||
await GenOutboundTransport(node, outbound);
|
await GenOutboundTransport(node, outbound);
|
||||||
|
|
@ -134,23 +136,28 @@ public partial class CoreConfigSingboxService
|
||||||
}
|
}
|
||||||
case EConfigType.Hysteria2:
|
case EConfigType.Hysteria2:
|
||||||
{
|
{
|
||||||
outbound.password = node.Id;
|
outbound.password = node.Password;
|
||||||
|
|
||||||
if (node.Path.IsNotEmpty())
|
if (!protocolExtra.SalamanderPass.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
outbound.obfs = new()
|
outbound.obfs = new()
|
||||||
{
|
{
|
||||||
type = "salamander",
|
type = "salamander",
|
||||||
password = node.Path.TrimEx(),
|
password = protocolExtra.SalamanderPass.TrimEx(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
outbound.up_mbps = _config.HysteriaItem.UpMbps > 0 ? _config.HysteriaItem.UpMbps : null;
|
outbound.up_mbps = protocolExtra?.UpMbps is { } su and >= 0
|
||||||
outbound.down_mbps = _config.HysteriaItem.DownMbps > 0 ? _config.HysteriaItem.DownMbps : null;
|
? su
|
||||||
if (node.Ports.IsNotEmpty() && (node.Ports.Contains(':') || node.Ports.Contains('-') || node.Ports.Contains(',')))
|
: _config.HysteriaItem.UpMbps;
|
||||||
|
outbound.down_mbps = protocolExtra?.DownMbps is { } sd and >= 0
|
||||||
|
? sd
|
||||||
|
: _config.HysteriaItem.DownMbps;
|
||||||
|
var ports = protocolExtra?.Ports?.IsNullOrEmpty() == false ? protocolExtra.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 =>
|
||||||
|
|
@ -159,21 +166,38 @@ public partial class CoreConfigSingboxService
|
||||||
return port.Contains(':') ? port : $"{port}:{port}";
|
return port.Contains(':') ? port : $"{port}:{port}";
|
||||||
})
|
})
|
||||||
.ToList();
|
.ToList();
|
||||||
outbound.hop_interval = _config.HysteriaItem.HopInterval > 0 ? $"{_config.HysteriaItem.HopInterval}s" : null;
|
outbound.hop_interval = _config.HysteriaItem.HopInterval >= 5
|
||||||
|
? $"{_config.HysteriaItem.HopInterval}s"
|
||||||
|
: $"{Global.Hysteria2DefaultHopInt}s";
|
||||||
|
if (int.TryParse(protocolExtra.HopInterval, out var hiResult))
|
||||||
|
{
|
||||||
|
outbound.hop_interval = hiResult >= 5 ? $"{hiResult}s" : outbound.hop_interval;
|
||||||
|
}
|
||||||
|
else if (protocolExtra.HopInterval?.Contains('-') ?? false)
|
||||||
|
{
|
||||||
|
// may be a range like 5-10
|
||||||
|
var parts = protocolExtra.HopInterval.Split('-');
|
||||||
|
if (parts.Length == 2 && int.TryParse(parts[0], out var hiL) &&
|
||||||
|
int.TryParse(parts[0], out var hiH))
|
||||||
|
{
|
||||||
|
var hi = (hiL + hiH) / 2;
|
||||||
|
outbound.hop_interval = hi >= 5 ? $"{hi}s" : outbound.hop_interval;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EConfigType.TUIC:
|
case EConfigType.TUIC:
|
||||||
{
|
{
|
||||||
outbound.uuid = node.Id;
|
outbound.uuid = node.Username;
|
||||||
outbound.password = node.Security;
|
outbound.password = node.Password;
|
||||||
outbound.congestion_control = node.HeaderType;
|
outbound.congestion_control = node.HeaderType;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EConfigType.Anytls:
|
case EConfigType.Anytls:
|
||||||
{
|
{
|
||||||
outbound.password = node.Id;
|
outbound.password = node.Password;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -191,7 +215,9 @@ public partial class CoreConfigSingboxService
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
endpoint.address = Utils.String2List(node.RequestHost);
|
var protocolExtra = node.GetProtocolExtra();
|
||||||
|
|
||||||
|
endpoint.address = Utils.String2List(protocolExtra.WgInterfaceAddress);
|
||||||
endpoint.type = Global.ProtocolTypes[node.ConfigType];
|
endpoint.type = Global.ProtocolTypes[node.ConfigType];
|
||||||
|
|
||||||
switch (node.ConfigType)
|
switch (node.ConfigType)
|
||||||
|
|
@ -200,16 +226,17 @@ public partial class CoreConfigSingboxService
|
||||||
{
|
{
|
||||||
var peer = new Peer4Sbox
|
var peer = new Peer4Sbox
|
||||||
{
|
{
|
||||||
public_key = node.PublicKey,
|
public_key = protocolExtra.WgPublicKey,
|
||||||
reserved = Utils.String2List(node.Path)?.Select(int.Parse).ToList(),
|
pre_shared_key = protocolExtra.WgPresharedKey,
|
||||||
|
reserved = Utils.String2List(protocolExtra.WgReserved)?.Select(int.Parse).ToList(),
|
||||||
address = node.Address,
|
address = node.Address,
|
||||||
port = node.Port,
|
port = node.Port,
|
||||||
// TODO default ["0.0.0.0/0", "::/0"]
|
// TODO default ["0.0.0.0/0", "::/0"]
|
||||||
allowed_ips = new() { "0.0.0.0/0", "::/0" },
|
allowed_ips = new() { "0.0.0.0/0", "::/0" },
|
||||||
};
|
};
|
||||||
endpoint.private_key = node.Id;
|
endpoint.private_key = node.Password;
|
||||||
endpoint.mtu = node.ShortId.IsNullOrEmpty() ? Global.TunMtus.First() : node.ShortId.ToInt();
|
endpoint.mtu = protocolExtra.WgMtu > 0 ? protocolExtra.WgMtu : Global.TunMtus.First();
|
||||||
endpoint.peers = new() { peer };
|
endpoint.peers = [peer];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -453,13 +480,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 +494,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 +613,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 +622,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 +792,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 +801,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 protocolExtra = node.GetProtocolExtra();
|
||||||
var muxEnabled = node.MuxEnabled ?? _config.CoreBasicItem.MuxEnabled;
|
var muxEnabled = node.MuxEnabled ?? _config.CoreBasicItem.MuxEnabled;
|
||||||
switch (node.ConfigType)
|
switch (node.ConfigType)
|
||||||
{
|
{
|
||||||
|
|
@ -35,12 +36,12 @@ public partial class CoreConfigV2rayService
|
||||||
usersItem = vnextItem.users.First();
|
usersItem = vnextItem.users.First();
|
||||||
}
|
}
|
||||||
|
|
||||||
usersItem.id = node.Id;
|
usersItem.id = node.Password;
|
||||||
usersItem.alterId = node.AlterId;
|
usersItem.alterId = int.TryParse(protocolExtra?.AlterId, out var result) ? result : 0;
|
||||||
usersItem.email = Global.UserEMail;
|
usersItem.email = Global.UserEMail;
|
||||||
if (Global.VmessSecurities.Contains(node.Security))
|
if (Global.VmessSecurities.Contains(protocolExtra.VmessSecurity))
|
||||||
{
|
{
|
||||||
usersItem.security = node.Security;
|
usersItem.security = protocolExtra.VmessSecurity;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -66,8 +67,9 @@ public partial class CoreConfigV2rayService
|
||||||
}
|
}
|
||||||
serversItem.address = node.Address;
|
serversItem.address = node.Address;
|
||||||
serversItem.port = node.Port;
|
serversItem.port = node.Port;
|
||||||
serversItem.password = node.Id;
|
serversItem.password = node.Password;
|
||||||
serversItem.method = AppManager.Instance.GetShadowsocksSecurities(node).Contains(node.Security) ? node.Security : "none";
|
serversItem.method = AppManager.Instance.GetShadowsocksSecurities(node).Contains(protocolExtra.SsMethod)
|
||||||
|
? protocolExtra.SsMethod : "none";
|
||||||
|
|
||||||
serversItem.ota = false;
|
serversItem.ota = false;
|
||||||
serversItem.level = 1;
|
serversItem.level = 1;
|
||||||
|
|
@ -95,13 +97,13 @@ public partial class CoreConfigV2rayService
|
||||||
serversItem.method = null;
|
serversItem.method = null;
|
||||||
serversItem.password = null;
|
serversItem.password = null;
|
||||||
|
|
||||||
if (node.Security.IsNotEmpty()
|
if (node.Username.IsNotEmpty()
|
||||||
&& node.Id.IsNotEmpty())
|
&& node.Password.IsNotEmpty())
|
||||||
{
|
{
|
||||||
SocksUsersItem4Ray socksUsersItem = new()
|
SocksUsersItem4Ray socksUsersItem = new()
|
||||||
{
|
{
|
||||||
user = node.Security,
|
user = node.Username ?? "",
|
||||||
pass = node.Id,
|
pass = node.Password,
|
||||||
level = 1
|
level = 1
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -138,17 +140,16 @@ public partial class CoreConfigV2rayService
|
||||||
{
|
{
|
||||||
usersItem = vnextItem.users.First();
|
usersItem = vnextItem.users.First();
|
||||||
}
|
}
|
||||||
usersItem.id = node.Id;
|
usersItem.id = node.Password;
|
||||||
usersItem.email = Global.UserEMail;
|
usersItem.email = Global.UserEMail;
|
||||||
usersItem.encryption = node.Security;
|
usersItem.encryption = protocolExtra.VlessEncryption;
|
||||||
|
|
||||||
if (node.Flow.IsNullOrEmpty())
|
if (!protocolExtra.Flow.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
await GenOutboundMux(node, outbound, muxEnabled, muxEnabled);
|
usersItem.flow = protocolExtra.Flow;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
usersItem.flow = node.Flow;
|
|
||||||
await GenOutboundMux(node, outbound, false, muxEnabled);
|
await GenOutboundMux(node, outbound, false, muxEnabled);
|
||||||
}
|
}
|
||||||
outbound.settings.servers = null;
|
outbound.settings.servers = null;
|
||||||
|
|
@ -168,7 +169,7 @@ public partial class CoreConfigV2rayService
|
||||||
}
|
}
|
||||||
serversItem.address = node.Address;
|
serversItem.address = node.Address;
|
||||||
serversItem.port = node.Port;
|
serversItem.port = node.Port;
|
||||||
serversItem.password = node.Id;
|
serversItem.password = node.Password;
|
||||||
|
|
||||||
serversItem.ota = false;
|
serversItem.ota = false;
|
||||||
serversItem.level = 1;
|
serversItem.level = 1;
|
||||||
|
|
@ -199,16 +200,16 @@ public partial class CoreConfigV2rayService
|
||||||
}
|
}
|
||||||
var peer = new WireguardPeer4Ray
|
var peer = new WireguardPeer4Ray
|
||||||
{
|
{
|
||||||
publicKey = node.PublicKey,
|
publicKey = protocolExtra.WgPublicKey ?? "",
|
||||||
endpoint = address + ":" + node.Port.ToString()
|
endpoint = address + ":" + node.Port.ToString()
|
||||||
};
|
};
|
||||||
var setting = new Outboundsettings4Ray
|
var setting = new Outboundsettings4Ray
|
||||||
{
|
{
|
||||||
address = Utils.String2List(node.RequestHost),
|
address = Utils.String2List(protocolExtra.WgInterfaceAddress),
|
||||||
secretKey = node.Id,
|
secretKey = node.Password,
|
||||||
reserved = Utils.String2List(node.Path)?.Select(int.Parse).ToList(),
|
reserved = Utils.String2List(protocolExtra.WgReserved)?.Select(int.Parse).ToList(),
|
||||||
mtu = node.ShortId.IsNullOrEmpty() ? Global.TunMtus.First() : node.ShortId.ToInt(),
|
mtu = protocolExtra.WgMtu > 0 ? protocolExtra.WgMtu : Global.TunMtus.First(),
|
||||||
peers = new List<WireguardPeer4Ray> { peer }
|
peers = [peer]
|
||||||
};
|
};
|
||||||
outbound.settings = setting;
|
outbound.settings = setting;
|
||||||
outbound.settings.vnext = null;
|
outbound.settings.vnext = null;
|
||||||
|
|
@ -509,28 +510,38 @@ public partial class CoreConfigV2rayService
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "hysteria":
|
case "hysteria":
|
||||||
HysteriaUdpHop4Ray? udpHop = null;
|
var protocolExtra = node.GetProtocolExtra();
|
||||||
if (node.Ports.IsNotEmpty() &&
|
var ports = protocolExtra?.Ports;
|
||||||
(node.Ports.Contains(':') || node.Ports.Contains('-') || node.Ports.Contains(',')))
|
int? upMbps = protocolExtra?.UpMbps is { } su and >= 0
|
||||||
{
|
? su
|
||||||
udpHop = new()
|
: _config.HysteriaItem.UpMbps;
|
||||||
{
|
int? downMbps = protocolExtra?.DownMbps is { } sd and >= 0
|
||||||
ports = node.Ports.Replace(':', '-'),
|
? sd
|
||||||
interval = _config.HysteriaItem.HopInterval > 0
|
: _config.HysteriaItem.UpMbps;
|
||||||
|
var hopInterval = !protocolExtra.HopInterval.IsNullOrEmpty()
|
||||||
|
? protocolExtra.HopInterval
|
||||||
|
: (_config.HysteriaItem.HopInterval >= 5
|
||||||
? _config.HysteriaItem.HopInterval
|
? _config.HysteriaItem.HopInterval
|
||||||
: null,
|
: Global.Hysteria2DefaultHopInt).ToString();
|
||||||
|
HysteriaUdpHop4Ray? udpHop = null;
|
||||||
|
if (!ports.IsNullOrEmpty() &&
|
||||||
|
(ports.Contains(':') || ports.Contains('-') || ports.Contains(',')))
|
||||||
|
{
|
||||||
|
udpHop = new HysteriaUdpHop4Ray
|
||||||
|
{
|
||||||
|
ports = ports.Replace(':', '-'),
|
||||||
|
interval = hopInterval,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
HysteriaSettings4Ray hysteriaSettings = new()
|
streamSettings.hysteriaSettings = new()
|
||||||
{
|
{
|
||||||
version = 2,
|
version = 2,
|
||||||
auth = node.Id,
|
auth = node.Password,
|
||||||
up = _config.HysteriaItem.UpMbps > 0 ? $"{_config.HysteriaItem.UpMbps}mbps" : null,
|
up = upMbps > 0 ? $"{upMbps}mbps" : null,
|
||||||
down = _config.HysteriaItem.DownMbps > 0 ? $"{_config.HysteriaItem.DownMbps}mbps" : null,
|
down = downMbps > 0 ? $"{downMbps}mbps" : null,
|
||||||
udphop = udpHop,
|
udphop = udpHop,
|
||||||
};
|
};
|
||||||
streamSettings.hysteriaSettings = hysteriaSettings;
|
if (!protocolExtra.SalamanderPass.IsNullOrEmpty())
|
||||||
if (node.Path.IsNotEmpty())
|
|
||||||
{
|
{
|
||||||
streamSettings.finalmask ??= new();
|
streamSettings.finalmask ??= new();
|
||||||
streamSettings.finalmask.udp =
|
streamSettings.finalmask.udp =
|
||||||
|
|
@ -538,7 +549,7 @@ public partial class CoreConfigV2rayService
|
||||||
new Mask4Ray
|
new Mask4Ray
|
||||||
{
|
{
|
||||||
type = "salamander",
|
type = "salamander",
|
||||||
settings = new MaskSettings4Ray { password = node.Path.TrimEx(), }
|
settings = new MaskSettings4Ray { password = protocolExtra.SalamanderPass.TrimEx(), }
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
@ -592,13 +603,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 +638,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 +749,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 +903,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 protocolExtra = SelectedSource.GetProtocolExtra();
|
||||||
PolicyGroupType = (profileGroup?.MultipleLoad ?? EMultipleLoad.LeastPing) switch
|
PolicyGroupType = (protocolExtra?.MultipleLoad ?? EMultipleLoad.LeastPing) switch
|
||||||
{
|
{
|
||||||
EMultipleLoad.LeastPing => ResUI.TbLeastPing,
|
EMultipleLoad.LeastPing => ResUI.TbLeastPing,
|
||||||
EMultipleLoad.Fallback => ResUI.TbFallback,
|
EMultipleLoad.Fallback => ResUI.TbFallback,
|
||||||
|
|
@ -93,13 +93,10 @@ 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 == protocolExtra?.SubChildItems);
|
||||||
Filter = profileGroup?.Filter;
|
Filter = protocolExtra?.Filter;
|
||||||
|
|
||||||
var childItemMulti = ProfileGroupItemManager.Instance.GetOrCreateAndMarkDirty(SelectedSource?.IndexId);
|
var childIndexIds = Utils.String2List(protocolExtra?.ChildItems) ?? [];
|
||||||
if (childItemMulti != null)
|
|
||||||
{
|
|
||||||
var childIndexIds = Utils.String2List(childItemMulti.ChildItems) ?? [];
|
|
||||||
foreach (var item in childIndexIds)
|
foreach (var item in childIndexIds)
|
||||||
{
|
{
|
||||||
var child = await AppManager.Instance.GetProfileItem(item);
|
var child = await AppManager.Instance.GetProfileItem(item);
|
||||||
|
|
@ -110,7 +107,6 @@ public class AddGroupServerViewModel : MyReactiveObject
|
||||||
ChildItemsObs.Add(child);
|
ChildItemsObs.Add(child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public async Task ChildRemoveAsync()
|
public async Task ChildRemoveAsync()
|
||||||
{
|
{
|
||||||
|
|
@ -205,18 +201,12 @@ public class AddGroupServerViewModel : MyReactiveObject
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var childIndexIds = new List<string>();
|
|
||||||
foreach (var item in ChildItemsObs)
|
SelectedSource.SetProtocolExtra(SelectedSource.GetProtocolExtra() with
|
||||||
{
|
{
|
||||||
if (item.IndexId.IsNullOrEmpty())
|
ChildItems =
|
||||||
{
|
Utils.List2String(ChildItemsObs.Where(s => !s.IndexId.IsNullOrEmpty()).Select(s => s.IndexId).ToList()),
|
||||||
continue;
|
MultipleLoad = PolicyGroupType switch
|
||||||
}
|
|
||||||
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,
|
||||||
|
|
@ -224,19 +214,19 @@ public class AddGroupServerViewModel : MyReactiveObject
|
||||||
var s when s == ResUI.TbRoundRobin => EMultipleLoad.RoundRobin,
|
var s when s == ResUI.TbRoundRobin => EMultipleLoad.RoundRobin,
|
||||||
var s when s == ResUI.TbLeastLoad => EMultipleLoad.LeastLoad,
|
var s when s == ResUI.TbLeastLoad => EMultipleLoad.LeastLoad,
|
||||||
_ => EMultipleLoad.LeastPing,
|
_ => EMultipleLoad.LeastPing,
|
||||||
};
|
},
|
||||||
|
SubChildItems = SelectedSubItem?.Id,
|
||||||
|
Filter = Filter,
|
||||||
|
});
|
||||||
|
|
||||||
profileGroup.SubChildItems = SelectedSubItem?.Id;
|
var hasCycle = await GroupProfileManager.HasCycle(SelectedSource.IndexId, SelectedSource.GetProtocolExtra());
|
||||||
profileGroup.Filter = Filter;
|
|
||||||
|
|
||||||
var hasCycle = ProfileGroupItemManager.HasCycle(profileGroup.IndexId);
|
|
||||||
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)
|
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,47 @@ public class AddServerViewModel : MyReactiveObject
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public string CertSha { get; set; }
|
public string CertSha { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public string SalamanderPass { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public int AlterId { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public string Ports { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public int UpMbps { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public int DownMbps { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public string HopInterval { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public string Flow { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public string VmessSecurity { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public string VlessEncryption { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public string SsMethod { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public string WgPublicKey { get; set; }
|
||||||
|
//[Reactive]
|
||||||
|
//public string WgPresharedKey { get; set; }
|
||||||
|
[Reactive]
|
||||||
|
public string WgInterfaceAddress { get; set; }
|
||||||
|
[Reactive]
|
||||||
|
public string WgReserved { get; set; }
|
||||||
|
[Reactive]
|
||||||
|
public int WgMtu { 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 +104,22 @@ 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 protocolExtra = SelectedSource?.GetProtocolExtra();
|
||||||
|
Ports = protocolExtra?.Ports ?? string.Empty;
|
||||||
|
AlterId = int.TryParse(protocolExtra?.AlterId, out var result) ? result : 0;
|
||||||
|
Flow = protocolExtra?.Flow ?? string.Empty;
|
||||||
|
SalamanderPass = protocolExtra?.SalamanderPass ?? string.Empty;
|
||||||
|
UpMbps = protocolExtra?.UpMbps ?? _config.HysteriaItem.UpMbps;
|
||||||
|
DownMbps = protocolExtra?.DownMbps ?? _config.HysteriaItem.DownMbps;
|
||||||
|
HopInterval = protocolExtra?.HopInterval.IsNullOrEmpty() ?? true ? Global.Hysteria2DefaultHopInt.ToString() : protocolExtra.HopInterval;
|
||||||
|
VmessSecurity = protocolExtra?.VmessSecurity?.IsNullOrEmpty() == false ? protocolExtra.VmessSecurity : Global.DefaultSecurity;
|
||||||
|
VlessEncryption = protocolExtra?.VlessEncryption.IsNullOrEmpty() == false ? protocolExtra.VlessEncryption : Global.None;
|
||||||
|
SsMethod = protocolExtra?.SsMethod ?? string.Empty;
|
||||||
|
WgPublicKey = protocolExtra?.WgPublicKey ?? string.Empty;
|
||||||
|
WgInterfaceAddress = protocolExtra?.WgInterfaceAddress ?? string.Empty;
|
||||||
|
WgReserved = protocolExtra?.WgReserved ?? string.Empty;
|
||||||
|
WgMtu = protocolExtra?.WgMtu ?? 1280;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SaveServerAsync()
|
private async Task SaveServerAsync()
|
||||||
|
|
@ -87,12 +144,12 @@ public class AddServerViewModel : MyReactiveObject
|
||||||
}
|
}
|
||||||
if (SelectedSource.ConfigType == EConfigType.Shadowsocks)
|
if (SelectedSource.ConfigType == EConfigType.Shadowsocks)
|
||||||
{
|
{
|
||||||
if (SelectedSource.Id.IsNullOrEmpty())
|
if (SelectedSource.Password.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
NoticeManager.Instance.Enqueue(ResUI.FillPassword);
|
NoticeManager.Instance.Enqueue(ResUI.FillPassword);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (SelectedSource.Security.IsNullOrEmpty())
|
if (SsMethod.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
NoticeManager.Instance.Enqueue(ResUI.PleaseSelectEncryption);
|
NoticeManager.Instance.Enqueue(ResUI.PleaseSelectEncryption);
|
||||||
return;
|
return;
|
||||||
|
|
@ -100,7 +157,7 @@ public class AddServerViewModel : MyReactiveObject
|
||||||
}
|
}
|
||||||
if (SelectedSource.ConfigType is not EConfigType.SOCKS and not EConfigType.HTTP)
|
if (SelectedSource.ConfigType is not EConfigType.SOCKS and not EConfigType.HTTP)
|
||||||
{
|
{
|
||||||
if (SelectedSource.Id.IsNullOrEmpty())
|
if (SelectedSource.Password.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
NoticeManager.Instance.Enqueue(ResUI.FillUUID);
|
NoticeManager.Instance.Enqueue(ResUI.FillUUID);
|
||||||
return;
|
return;
|
||||||
|
|
@ -109,6 +166,23 @@ 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;
|
||||||
|
SelectedSource.SetProtocolExtra(SelectedSource.GetProtocolExtra() with
|
||||||
|
{
|
||||||
|
Ports = Ports.NullIfEmpty(),
|
||||||
|
AlterId = AlterId > 0 ? AlterId.ToString() : null,
|
||||||
|
Flow = Flow.NullIfEmpty(),
|
||||||
|
SalamanderPass = SalamanderPass.NullIfEmpty(),
|
||||||
|
UpMbps = UpMbps >= 0 ? UpMbps : null,
|
||||||
|
DownMbps = DownMbps >= 0 ? DownMbps : null,
|
||||||
|
HopInterval = HopInterval.NullIfEmpty(),
|
||||||
|
VmessSecurity = VmessSecurity.NullIfEmpty(),
|
||||||
|
VlessEncryption = VlessEncryption.NullIfEmpty(),
|
||||||
|
SsMethod = SsMethod.NullIfEmpty(),
|
||||||
|
WgPublicKey = WgPublicKey.NullIfEmpty(),
|
||||||
|
WgInterfaceAddress = WgInterfaceAddress.NullIfEmpty(),
|
||||||
|
WgReserved = WgReserved.NullIfEmpty(),
|
||||||
|
WgMtu = WgMtu >= 576 ? WgMtu : null,
|
||||||
|
});
|
||||||
|
|
||||||
if (await ConfigHandler.AddServer(_config, SelectedSource) == 0)
|
if (await ConfigHandler.AddServer(_config, SelectedSource) == 0)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -259,7 +259,6 @@ 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);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -209,7 +209,7 @@ public class ProfilesSelectViewModel : MyReactiveObject
|
||||||
Remarks = t.Remarks,
|
Remarks = t.Remarks,
|
||||||
Address = t.Address,
|
Address = t.Address,
|
||||||
Port = t.Port,
|
Port = t.Port,
|
||||||
Security = t.Security,
|
//Security = t.Security,
|
||||||
Network = t.Network,
|
Network = t.Network,
|
||||||
StreamSecurity = t.StreamSecurity,
|
StreamSecurity = t.StreamSecurity,
|
||||||
Subid = t.Subid,
|
Subid = t.Subid,
|
||||||
|
|
|
||||||
|
|
@ -446,7 +446,7 @@ public class ProfilesViewModel : MyReactiveObject
|
||||||
Remarks = t.Remarks,
|
Remarks = t.Remarks,
|
||||||
Address = t.Address,
|
Address = t.Address,
|
||||||
Port = t.Port,
|
Port = t.Port,
|
||||||
Security = t.Security,
|
//Security = t.Security,
|
||||||
Network = t.Network,
|
Network = t.Network,
|
||||||
StreamSecurity = t.StreamSecurity,
|
StreamSecurity = t.StreamSecurity,
|
||||||
Subid = t.Subid,
|
Subid = t.Subid,
|
||||||
|
|
|
||||||
|
|
@ -360,7 +360,7 @@
|
||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
ColumnDefinitions="300,Auto,Auto"
|
ColumnDefinitions="300,Auto,Auto"
|
||||||
IsVisible="False"
|
IsVisible="False"
|
||||||
RowDefinitions="Auto,Auto,Auto,Auto">
|
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto">
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
|
|
@ -407,6 +407,41 @@
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Text="{x:Static resx:ResUI.TbPorts7Tips}" />
|
Text="{x:Static resx:ResUI.TbPorts7Tips}" />
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="4"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{x:Static resx:ResUI.TbHopInt7}" />
|
||||||
|
<TextBox
|
||||||
|
x:Name="txtHopInt7"
|
||||||
|
Grid.Row="4"
|
||||||
|
Grid.Column="1"
|
||||||
|
Width="400"
|
||||||
|
Margin="{StaticResource Margin4}" />
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="5"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{x:Static resx:ResUI.TbSettingsHysteriaBandwidth}" />
|
||||||
|
<StackPanel
|
||||||
|
Grid.Row="5"
|
||||||
|
Grid.Column="1"
|
||||||
|
Orientation="Horizontal">
|
||||||
|
<TextBox
|
||||||
|
x:Name="txtUpMbps7"
|
||||||
|
Width="90"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
Watermark="Up" />
|
||||||
|
<TextBox
|
||||||
|
x:Name="txtDownMbps7"
|
||||||
|
Width="90"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
Watermark="Down" />
|
||||||
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid
|
<Grid
|
||||||
x:Name="gridTuic"
|
x:Name="gridTuic"
|
||||||
|
|
|
||||||
|
|
@ -39,10 +39,6 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
||||||
case EConfigType.VMess:
|
case EConfigType.VMess:
|
||||||
gridVMess.IsVisible = true;
|
gridVMess.IsVisible = true;
|
||||||
cmbSecurity.ItemsSource = Global.VmessSecurities;
|
cmbSecurity.ItemsSource = Global.VmessSecurities;
|
||||||
if (profileItem.Security.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
profileItem.Security = Global.DefaultSecurity;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.Shadowsocks:
|
case EConfigType.Shadowsocks:
|
||||||
|
|
@ -59,10 +55,6 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
||||||
gridVLESS.IsVisible = true;
|
gridVLESS.IsVisible = true;
|
||||||
lstStreamSecurity.Add(Global.StreamSecurityReality);
|
lstStreamSecurity.Add(Global.StreamSecurityReality);
|
||||||
cmbFlow5.ItemsSource = Global.Flows;
|
cmbFlow5.ItemsSource = Global.Flows;
|
||||||
if (profileItem.Security.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
profileItem.Security = Global.None;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.Trojan:
|
case EConfigType.Trojan:
|
||||||
|
|
@ -119,59 +111,62 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
||||||
switch (profileItem.ConfigType)
|
switch (profileItem.ConfigType)
|
||||||
{
|
{
|
||||||
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.Password, 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.VmessSecurity, 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;
|
||||||
|
|
||||||
case EConfigType.Shadowsocks:
|
case EConfigType.Shadowsocks:
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId3.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId3.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.cmbSecurity3.SelectedValue).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SsMethod, v => v.cmbSecurity3.SelectedValue).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled3.IsChecked).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled3.IsChecked).DisposeWith(disposables);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.SOCKS:
|
case EConfigType.SOCKS:
|
||||||
case EConfigType.HTTP:
|
case EConfigType.HTTP:
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId4.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId4.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.txtSecurity4.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtSecurity4.Text).DisposeWith(disposables);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
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.Password, 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.VlessEncryption, 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.Password, 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.Password, v => v.txtId7.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Path, v => v.txtPath7.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SalamanderPass, 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);
|
||||||
|
this.Bind(ViewModel, vm => vm.HopInterval, v => v.txtHopInt7.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.UpMbps, v => v.txtUpMbps7.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.DownMbps, v => v.txtDownMbps7.Text).DisposeWith(disposables);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.TUIC:
|
case EConfigType.TUIC:
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId8.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtId8.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.txtSecurity8.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtSecurity8.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType8.SelectedValue).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType8.SelectedValue).DisposeWith(disposables);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.WireGuard:
|
case EConfigType.WireGuard:
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId9.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId9.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.PublicKey, v => v.txtPublicKey9.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.WgPublicKey, v => v.txtPublicKey9.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Path, v => v.txtPath9.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.WgReserved, v => v.txtPath9.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.RequestHost, v => v.txtRequestHost9.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.WgInterfaceAddress, v => v.txtRequestHost9.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.ShortId, v => v.txtShortId9.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.WgMtu, v => v.txtShortId9.Text).DisposeWith(disposables);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.Anytls:
|
case EConfigType.Anytls:
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId10.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId10.Text).DisposeWith(disposables);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.SelectedValue).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.SelectedValue).DisposeWith(disposables);
|
||||||
|
|
|
||||||
|
|
@ -487,6 +487,8 @@
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="300" />
|
<ColumnDefinition Width="300" />
|
||||||
|
|
@ -547,6 +549,47 @@
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Style="{StaticResource ToolbarTextBlock}"
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
Text="{x:Static resx:ResUI.TbPorts7Tips}" />
|
Text="{x:Static resx:ResUI.TbPorts7Tips}" />
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="4"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
|
Text="{x:Static resx:ResUI.TbHopInt7}" />
|
||||||
|
<TextBox
|
||||||
|
x:Name="txtHopInt7"
|
||||||
|
Grid.Row="4"
|
||||||
|
Grid.Column="1"
|
||||||
|
Width="200"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
Style="{StaticResource DefTextBox}" />
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="5"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
|
Text="{x:Static resx:ResUI.TbSettingsHysteriaBandwidth}" />
|
||||||
|
<StackPanel
|
||||||
|
Grid.Row="5"
|
||||||
|
Grid.Column="1"
|
||||||
|
Orientation="Horizontal">
|
||||||
|
<TextBox
|
||||||
|
x:Name="txtUpMbps7"
|
||||||
|
Width="90"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
materialDesign:HintAssist.Hint="Up"
|
||||||
|
Style="{StaticResource DefTextBox}" />
|
||||||
|
<TextBox
|
||||||
|
x:Name="txtDownMbps7"
|
||||||
|
Width="90"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
materialDesign:HintAssist.Hint="Down"
|
||||||
|
Style="{StaticResource DefTextBox}" />
|
||||||
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid
|
<Grid
|
||||||
x:Name="gridTuic"
|
x:Name="gridTuic"
|
||||||
|
|
|
||||||
|
|
@ -34,10 +34,6 @@ public partial class AddServerWindow
|
||||||
case EConfigType.VMess:
|
case EConfigType.VMess:
|
||||||
gridVMess.Visibility = Visibility.Visible;
|
gridVMess.Visibility = Visibility.Visible;
|
||||||
cmbSecurity.ItemsSource = Global.VmessSecurities;
|
cmbSecurity.ItemsSource = Global.VmessSecurities;
|
||||||
if (profileItem.Security.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
profileItem.Security = Global.DefaultSecurity;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.Shadowsocks:
|
case EConfigType.Shadowsocks:
|
||||||
|
|
@ -54,10 +50,6 @@ public partial class AddServerWindow
|
||||||
gridVLESS.Visibility = Visibility.Visible;
|
gridVLESS.Visibility = Visibility.Visible;
|
||||||
lstStreamSecurity.Add(Global.StreamSecurityReality);
|
lstStreamSecurity.Add(Global.StreamSecurityReality);
|
||||||
cmbFlow5.ItemsSource = Global.Flows;
|
cmbFlow5.ItemsSource = Global.Flows;
|
||||||
if (profileItem.Security.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
profileItem.Security = Global.None;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.Trojan:
|
case EConfigType.Trojan:
|
||||||
|
|
@ -114,59 +106,62 @@ public partial class AddServerWindow
|
||||||
switch (profileItem.ConfigType)
|
switch (profileItem.ConfigType)
|
||||||
{
|
{
|
||||||
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.Password, 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.VmessSecurity, 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;
|
||||||
|
|
||||||
case EConfigType.Shadowsocks:
|
case EConfigType.Shadowsocks:
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId3.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId3.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.cmbSecurity3.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SsMethod, v => v.cmbSecurity3.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled3.IsChecked).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled3.IsChecked).DisposeWith(disposables);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.SOCKS:
|
case EConfigType.SOCKS:
|
||||||
case EConfigType.HTTP:
|
case EConfigType.HTTP:
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId4.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId4.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.txtSecurity4.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtSecurity4.Text).DisposeWith(disposables);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
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.Password, 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.VlessEncryption, 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.Password, 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.Password, v => v.txtId7.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Path, v => v.txtPath7.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SalamanderPass, 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);
|
||||||
|
this.Bind(ViewModel, vm => vm.HopInterval, v => v.txtHopInt7.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.UpMbps, v => v.txtUpMbps7.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.DownMbps, v => v.txtDownMbps7.Text).DisposeWith(disposables);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.TUIC:
|
case EConfigType.TUIC:
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId8.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtId8.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.txtSecurity8.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtSecurity8.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType8.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType8.Text).DisposeWith(disposables);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.WireGuard:
|
case EConfigType.WireGuard:
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId9.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId9.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.PublicKey, v => v.txtPublicKey9.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.WgPublicKey, v => v.txtPublicKey9.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Path, v => v.txtPath9.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.WgReserved, v => v.txtPath9.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.RequestHost, v => v.txtRequestHost9.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.WgInterfaceAddress, v => v.txtRequestHost9.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.ShortId, v => v.txtShortId9.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.WgMtu, v => v.txtShortId9.Text).DisposeWith(disposables);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.Anytls:
|
case EConfigType.Anytls:
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId10.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId10.Text).DisposeWith(disposables);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.Text).DisposeWith(disposables);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue