Compare commits

..

18 commits

Author SHA1 Message Date
DHR60
f36358dca8
Merge 6b1d827dd1 into bceebc1661 2026-02-05 16:55:51 +08:00
DHR60
6b1d827dd1 Group Preview 2026-02-05 16:26:38 +08:00
DHR60
1b79d2f560 Fix group 2026-02-05 16:26:22 +08:00
DHR60
fa8c3f337c Fix Tuic 2026-02-05 15:28:02 +08:00
DHR60
314fb7c73d Hy2 SalamanderPass 2026-02-05 13:15:04 +08:00
DHR60
37734d2de9 Refactor, use record instead of class 2026-02-05 12:51:40 +08:00
2dust
3b826e3e36 Refactor ProfileItem protocol extra handling 2026-02-05 12:51:40 +08:00
DHR60
323bf70474 Refactor 2026-02-05 12:51:40 +08:00
DHR60
274fc8084d Fix 2026-02-05 12:51:39 +08:00
DHR60
2c454aee95 Fix hy2 migrate 2026-02-05 12:51:39 +08:00
DHR60
0eae23ace0 Remove unused code 2026-02-05 12:51:39 +08:00
DHR60
189ab47fa9 Fix warning CS0618 2026-02-05 12:51:39 +08:00
DHR60
7bfd2f504d Fix hy2 bbr 2026-02-05 12:51:39 +08:00
DHR60
82443a64cd Refactor flow 2026-02-05 12:51:39 +08:00
DHR60
ee7d3c60b6 Refactor id and security 2026-02-05 12:51:39 +08:00
DHR60
2d450357d1 Upgrade config version and rename 2026-02-05 12:51:39 +08:00
DHR60
1c2d086a71 Add hysteria2 bandwidth and hop interval support 2026-02-05 12:51:39 +08:00
DHR60
a6b12aa718 Refactor 2026-02-05 12:50:04 +08:00
18 changed files with 133 additions and 177 deletions

View file

@ -332,20 +332,22 @@ public class Utils
.ToList(); .ToList();
} }
public static Dictionary<string, List<string>> ParseHostsToDictionary(string? hostsContent) public static Dictionary<string, List<string>> ParseHostsToDictionary(string hostsContent)
{ {
if (hostsContent.IsNullOrEmpty())
{
return new();
}
var userHostsMap = hostsContent var userHostsMap = hostsContent
.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries) .Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
.Select(line => line.Trim()) .Select(line => line.Trim())
// skip full-line comments // skip full-line comments
.Where(line => !string.IsNullOrWhiteSpace(line) && !line.StartsWith('#')) .Where(line => !string.IsNullOrWhiteSpace(line) && !line.StartsWith("#"))
// strip inline comments (truncate at '#')
.Select(line =>
{
var index = line.IndexOf('#');
return index >= 0 ? line.Substring(0, index).Trim() : line;
})
// ensure line still contains valid parts // ensure line still contains valid parts
.Where(line => !string.IsNullOrWhiteSpace(line) && line.Contains(' ')) .Where(line => !string.IsNullOrWhiteSpace(line) && line.Contains(' '))
.Select(line => line.Split([' ', '\t'], StringSplitOptions.RemoveEmptyEntries)) .Select(line => line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries))
.Where(parts => parts.Length >= 2) .Where(parts => parts.Length >= 2)
.GroupBy(parts => parts[0]) .GroupBy(parts => parts[0])
.ToDictionary( .ToDictionary(

View file

@ -722,7 +722,7 @@ public static class ConfigHandler
SalamanderPass = profileItem.GetProtocolExtra().SalamanderPass?.TrimEx(), SalamanderPass = profileItem.GetProtocolExtra().SalamanderPass?.TrimEx(),
UpMbps = profileItem.GetProtocolExtra().UpMbps is null or < 0 ? config.HysteriaItem.UpMbps : profileItem.GetProtocolExtra().UpMbps, 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, DownMbps = profileItem.GetProtocolExtra().DownMbps is null or < 0 ? config.HysteriaItem.DownMbps : profileItem.GetProtocolExtra().DownMbps,
HopInterval = profileItem.GetProtocolExtra().HopInterval?.TrimEx(), HopInterval = profileItem.GetProtocolExtra().HopInterval is null or <= 5 ? Global.Hysteria2DefaultHopInt : profileItem.GetProtocolExtra().HopInterval,
}); });
await AddServerCommon(config, profileItem, toFile); await AddServerCommon(config, profileItem, toFile);
@ -745,8 +745,11 @@ 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.Username = profileItem.Username.TrimEx();
profileItem.Password = profileItem.Password.TrimEx(); profileItem.Password = profileItem.Password.TrimEx();
profileItem.SetProtocolExtra(profileItem.GetProtocolExtra() with
{
Username = profileItem.GetProtocolExtra().Username?.TrimEx()
});
profileItem.Network = string.Empty; profileItem.Network = string.Empty;
if (!Global.TuicCongestionControls.Contains(profileItem.HeaderType)) if (!Global.TuicCongestionControls.Contains(profileItem.HeaderType))

View file

@ -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.Username}:{item.Password}", true); var pw = Utils.Base64Encode($"{item.GetProtocolExtra().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,7 +78,7 @@ 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.Username = arr21.First(); item.SetProtocolExtra(item.GetProtocolExtra() with { Username = arr21.First() });
item.Password = arr21[1]; item.Password = arr21[1];
return item; return item;
} }
@ -103,7 +103,7 @@ public class SocksFmt : BaseFmt
var userInfoParts = userInfo.Split([':'], 2); var userInfoParts = userInfo.Split([':'], 2);
if (userInfoParts.Length == 2) if (userInfoParts.Length == 2)
{ {
item.Username = userInfoParts.First(); item.SetProtocolExtra(item.GetProtocolExtra() with { Username = userInfoParts.First() });
item.Password = userInfoParts[1]; item.Password = userInfoParts[1];
} }

View file

@ -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.Username = userInfoParts.First(); item.Password = userInfoParts.First();
item.Password = userInfoParts.Last(); item.SetProtocolExtra(item.GetProtocolExtra() with { Username = 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.Username ?? ""}:{item.Password}", dicQuery, remark); return ToUri(EConfigType.TUIC, item.Address, item.Port, $"{item.GetProtocolExtra().Username ?? ""}:{item.Password}", dicQuery, remark);
} }
} }

View file

@ -1,3 +1,5 @@
using ServiceLib.Common;
namespace ServiceLib.Manager; namespace ServiceLib.Manager;
public sealed class AppManager public sealed class AppManager
@ -96,10 +98,7 @@ public sealed class AppManager
_ = StatePort; _ = StatePort;
_ = StatePort2; _ = StatePort2;
Task.Run(async () => _ = MigrateProfileExtra();
{
await MigrateProfileExtra();
}).Wait();
return true; return true;
} }
@ -280,33 +279,29 @@ public sealed class AppManager
break; break;
} }
var batchSuccessCount = 0;
foreach (var item in batch) foreach (var item in batch)
{ {
try var extra = item.GetProtocolExtra();
{
var extra = item.GetProtocolExtra();
if (item.ConfigType is EConfigType.PolicyGroup or EConfigType.ProxyChain) 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 { GroupType = nameof(item.ConfigType) }; extra = extra with
groupItems.TryGetValue(item.IndexId, out var groupItem);
if (groupItem != null && !groupItem.NotHasChild())
{ {
extra = extra with ChildItems = groupItem.ChildItems,
{ SubChildItems = groupItem.SubChildItems,
ChildItems = groupItem.ChildItems, Filter = groupItem.Filter,
SubChildItems = groupItem.SubChildItems, MultipleLoad = groupItem.MultipleLoad,
Filter = groupItem.Filter, };
MultipleLoad = groupItem.MultipleLoad,
};
}
} }
switch (item.ConfigType) switch (item.ConfigType)
{ {
case EConfigType.Shadowsocks: case EConfigType.Shadowsocks:
extra = extra with { SsMethod = item.Security.NullIfEmpty() }; extra = extra with {SsMethod = item.Security.NullIfEmpty() };
break; break;
case EConfigType.VMess: case EConfigType.VMess:
extra = extra with extra = extra with
@ -319,7 +314,6 @@ public sealed class AppManager
extra = extra with extra = extra with
{ {
Flow = item.Flow.NullIfEmpty(), Flow = item.Flow.NullIfEmpty(),
VlessEncryption = item.Security,
}; };
break; break;
case EConfigType.Hysteria2: case EConfigType.Hysteria2:
@ -329,18 +323,17 @@ public sealed class AppManager
Ports = item.Ports.NullIfEmpty(), Ports = item.Ports.NullIfEmpty(),
UpMbps = _config.HysteriaItem.UpMbps, UpMbps = _config.HysteriaItem.UpMbps,
DownMbps = _config.HysteriaItem.DownMbps, DownMbps = _config.HysteriaItem.DownMbps,
HopInterval = _config.HysteriaItem.HopInterval.ToString(), HopInterval = _config.HysteriaItem.HopInterval
}; };
break; break;
case EConfigType.TUIC: case EConfigType.TUIC:
item.Username = item.Id; extra = extra with
{
Username = item.Id,
};
item.Id = item.Security; item.Id = item.Security;
item.Password = item.Security; item.Password = item.Security;
break; break;
case EConfigType.HTTP:
case EConfigType.SOCKS:
item.Username = item.Security;
break;
case EConfigType.WireGuard: case EConfigType.WireGuard:
extra = extra with extra = extra with
{ {
@ -351,24 +344,17 @@ public sealed class AppManager
}; };
break; 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}");
} }
item.SetProtocolExtra(extra);
item.Password = item.Id;
item.ConfigVersion = 3;
await SQLiteHelper.Instance.UpdateAsync(item);
} }
// Only increment offset by the number of failed items that remain in the result set offset += pageSize;
// Successfully updated items are automatically excluded from future queries due to ConfigVersion = 3
offset += batch.Count - batchSuccessCount;
} }
//await ProfileGroupItemManager.Instance.ClearAll(); //await ProfileGroupItemManager.Instance.ClearAll();

View file

@ -13,7 +13,6 @@ public class ProfileItem : ReactiveObject
Address = string.Empty; Address = string.Empty;
Port = 0; Port = 0;
Password = string.Empty; Password = string.Empty;
Username = string.Empty;
Network = string.Empty; Network = string.Empty;
Remarks = string.Empty; Remarks = string.Empty;
HeaderType = string.Empty; HeaderType = string.Empty;
@ -152,7 +151,6 @@ public class ProfileItem : ReactiveObject
public string Address { get; set; } public string Address { get; set; }
public int Port { get; set; } public int Port { get; set; }
public string Password { get; set; } public string Password { get; set; }
public string Username { 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; }

View file

@ -15,6 +15,9 @@ public record ProtocolExtraItem
//public string? PluginArgs { get; init; } //public string? PluginArgs { get; init; }
public string? SsMethod { get; init; } public string? SsMethod { get; init; }
// socks and http
public string? Username { get; init; }
// wireguard // wireguard
public string? WgPublicKey { get; init; } public string? WgPublicKey { get; init; }
public string? WgPresharedKey { get; init; } public string? WgPresharedKey { get; init; }
@ -27,7 +30,7 @@ public record ProtocolExtraItem
public int? UpMbps { get; init; } public int? UpMbps { get; init; }
public int? DownMbps { get; init; } public int? DownMbps { get; init; }
public string? Ports { get; init; } public string? Ports { get; init; }
public string? HopInterval { get; init; } public int? HopInterval { get; init; }
// group profile // group profile
public string? GroupType { get; init; } public string? GroupType { get; init; }

View file

@ -473,7 +473,7 @@ public class HysteriaSettings4Ray
public class HysteriaUdpHop4Ray public class HysteriaUdpHop4Ray
{ {
public string? ports { get; set; } public string? ports { get; set; }
public string? interval { get; set; } public int? interval { get; set; }
} }
public class FinalMask4Ray public class FinalMask4Ray

View file

@ -88,9 +88,14 @@ public partial class CoreConfigSingboxService
} }
} }
foreach (var kvp in Utils.ParseHostsToDictionary(simpleDNSItem.Hosts)) if (!simpleDNSItem.Hosts.IsNullOrEmpty())
{ {
hostsDns.predefined[kvp.Key] = kvp.Value.Where(s => Utils.IsIpAddress(s)).ToList(); var userHostsMap = Utils.ParseHostsToDictionary(simpleDNSItem.Hosts);
foreach (var kvp in userHostsMap)
{
hostsDns.predefined[kvp.Key] = kvp.Value;
}
} }
foreach (var host in hostsDns.predefined) foreach (var host in hostsDns.predefined)
@ -110,7 +115,7 @@ public partial class CoreConfigSingboxService
} }
singboxConfig.dns ??= new Dns4Sbox(); singboxConfig.dns ??= new Dns4Sbox();
singboxConfig.dns.servers ??= []; singboxConfig.dns.servers ??= new List<Server4Sbox>();
singboxConfig.dns.servers.Add(remoteDns); singboxConfig.dns.servers.Add(remoteDns);
singboxConfig.dns.servers.Add(directDns); singboxConfig.dns.servers.Add(directDns);
singboxConfig.dns.servers.Add(hostsDns); singboxConfig.dns.servers.Add(hostsDns);
@ -186,56 +191,13 @@ public partial class CoreConfigSingboxService
} }
}); });
foreach (var kvp in Utils.ParseHostsToDictionary(simpleDNSItem.Hosts))
{
var predefined = kvp.Value.First();
if (predefined.IsNullOrEmpty() || Utils.IsIpAddress(predefined))
{
continue;
}
if (predefined.StartsWith('#') && int.TryParse(predefined.AsSpan(1), out var rcode))
{
// xray syntactic sugar for predefined
// etc. #0 -> NOERROR
singboxConfig.dns.rules.Add(new()
{
query_type = [1, 28],
domain = [kvp.Key],
action = "predefined",
rcode = rcode switch
{
0 => "NOERROR",
1 => "FORMERR",
2 => "SERVFAIL",
3 => "NXDOMAIN",
4 => "NOTIMP",
5 => "REFUSED",
_ => "NOERROR",
},
});
continue;
}
// CNAME record
Rule4Sbox rule = new()
{
query_type = [1, 28],
action = "predefined",
rcode = "NOERROR",
answer = [$"*. IN CNAME {predefined}."],
};
if (ParseV2Domain(kvp.Key, rule))
{
singboxConfig.dns.rules.Add(rule);
}
}
var (ech, _) = ParseEchParam(node?.EchConfigList); var (ech, _) = ParseEchParam(node?.EchConfigList);
if (ech is not null) if (ech is not null)
{ {
var echDomain = ech.query_server_name ?? node?.Sni; var echDomain = ech.query_server_name ?? node?.Sni;
singboxConfig.dns.rules.Add(new() singboxConfig.dns.rules.Add(new()
{ {
query_type = [64, 65], query_type = new List<int> { 64, 65 },
server = Global.SingboxEchDNSTag, server = Global.SingboxEchDNSTag,
domain = echDomain is not null ? new List<string> { echDomain } : null, domain = echDomain is not null ? new List<string> { echDomain } : null,
}); });
@ -247,7 +209,7 @@ public partial class CoreConfigSingboxService
{ {
singboxConfig.dns.rules.Add(new() singboxConfig.dns.rules.Add(new()
{ {
query_type = [64, 65], query_type = new List<int> { 64, 65 },
server = Global.SingboxEchDNSTag, server = Global.SingboxEchDNSTag,
domain = queryServerNames, domain = queryServerNames,
}); });
@ -258,9 +220,9 @@ public partial class CoreConfigSingboxService
{ {
singboxConfig.dns.rules.Add(new() singboxConfig.dns.rules.Add(new()
{ {
query_type = [64, 65], query_type = new List<int> { 64, 65 },
action = "predefined", action = "predefined",
rcode = "NOERROR" rcode = "NOTIMP"
}); });
} }
@ -274,14 +236,13 @@ public partial class CoreConfigSingboxService
type = "logical", type = "logical",
mode = "and", mode = "and",
rewrite_ttl = 1, rewrite_ttl = 1,
rules = rules = new List<Rule4Sbox>
[ {
new() new() {
{ query_type = new List<int> { 1, 28 }, // A and AAAA
query_type = [1, 28], // A and AAAA
}, },
fakeipFilterRule fakeipFilterRule,
] }
}; };
singboxConfig.dns.rules.Add(rule4Fake); singboxConfig.dns.rules.Add(rule4Fake);
@ -301,7 +262,7 @@ public partial class CoreConfigSingboxService
if (!string.IsNullOrEmpty(simpleDNSItem?.DirectExpectedIPs)) if (!string.IsNullOrEmpty(simpleDNSItem?.DirectExpectedIPs))
{ {
var ipItems = simpleDNSItem.DirectExpectedIPs var ipItems = simpleDNSItem.DirectExpectedIPs
.Split([',', ';'], StringSplitOptions.RemoveEmptyEntries) .Split(new[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries)
.Select(s => s.Trim()) .Select(s => s.Trim())
.Where(s => !string.IsNullOrEmpty(s)) .Where(s => !string.IsNullOrEmpty(s))
.ToList(); .ToList();

View file

@ -90,20 +90,20 @@ public partial class CoreConfigSingboxService
case EConfigType.SOCKS: case EConfigType.SOCKS:
{ {
outbound.version = "5"; outbound.version = "5";
if (node.Username.IsNotEmpty() if (protocolExtra.Username.IsNotEmpty()
&& node.Password.IsNotEmpty()) && node.Password.IsNotEmpty())
{ {
outbound.username = node.Username; outbound.username = protocolExtra.Username;
outbound.password = node.Password; outbound.password = node.Password;
} }
break; break;
} }
case EConfigType.HTTP: case EConfigType.HTTP:
{ {
if (node.Username.IsNotEmpty() if (protocolExtra.Username.IsNotEmpty()
&& node.Password.IsNotEmpty()) && node.Password.IsNotEmpty())
{ {
outbound.username = node.Username; outbound.username = protocolExtra.Username;
outbound.password = node.Password; outbound.password = node.Password;
} }
break; break;
@ -114,13 +114,13 @@ public partial class CoreConfigSingboxService
outbound.packet_encoding = "xudp"; outbound.packet_encoding = "xudp";
if (!protocolExtra.Flow.IsNullOrEmpty()) if (protocolExtra.Flow.IsNullOrEmpty())
{ {
outbound.flow = protocolExtra.Flow; await GenOutboundMux(node, outbound);
} }
else else
{ {
await GenOutboundMux(node, outbound); outbound.flow = protocolExtra.Flow;
} }
await GenOutboundTransport(node, outbound); await GenOutboundTransport(node, outbound);
@ -149,10 +149,10 @@ public partial class CoreConfigSingboxService
outbound.up_mbps = protocolExtra?.UpMbps is { } su and >= 0 outbound.up_mbps = protocolExtra?.UpMbps is { } su and >= 0
? su ? su
: _config.HysteriaItem.UpMbps; : 0;
outbound.down_mbps = protocolExtra?.DownMbps is { } sd and >= 0 outbound.down_mbps = protocolExtra?.DownMbps is { } sd and >= 0
? sd ? sd
: _config.HysteriaItem.DownMbps; : 0;
var ports = protocolExtra?.Ports?.IsNullOrEmpty() == false ? protocolExtra.Ports : null; var ports = protocolExtra?.Ports?.IsNullOrEmpty() == false ? protocolExtra.Ports : null;
if ((!ports.IsNullOrEmpty()) && (ports.Contains(':') || ports.Contains('-') || ports.Contains(','))) if ((!ports.IsNullOrEmpty()) && (ports.Contains(':') || ports.Contains('-') || ports.Contains(',')))
{ {
@ -166,31 +166,16 @@ public partial class CoreConfigSingboxService
return port.Contains(':') ? port : $"{port}:{port}"; return port.Contains(':') ? port : $"{port}:{port}";
}) })
.ToList(); .ToList();
outbound.hop_interval = _config.HysteriaItem.HopInterval >= 5 outbound.hop_interval = protocolExtra?.HopInterval is { } hi and >= 5
? $"{_config.HysteriaItem.HopInterval}s" ? $"{hi}s"
: $"{Global.Hysteria2DefaultHopInt}s"; : _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.Username; outbound.uuid = protocolExtra.Username;
outbound.password = node.Password; outbound.password = node.Password;
outbound.congestion_control = node.HeaderType; outbound.congestion_control = node.HeaderType;
break; break;

View file

@ -75,8 +75,14 @@ public partial class CoreConfigSingboxService
var dnsItem = await AppManager.Instance.GetDNSItem(ECoreType.sing_box); var dnsItem = await AppManager.Instance.GetDNSItem(ECoreType.sing_box);
if (dnsItem == null || !dnsItem.Enabled) if (dnsItem == null || !dnsItem.Enabled)
{ {
var userHostsMap = Utils.ParseHostsToDictionary(simpleDnsItem.Hosts); if (!simpleDnsItem.Hosts.IsNullOrEmpty())
hostsDomains.AddRange(userHostsMap.Select(kvp => kvp.Key)); {
var userHostsMap = Utils.ParseHostsToDictionary(simpleDnsItem.Hosts);
foreach (var kvp in userHostsMap)
{
hostsDomains.Add(kvp.Key);
}
}
if (simpleDnsItem.UseSystemHosts == true) if (simpleDnsItem.UseSystemHosts == true)
{ {
var systemHostsMap = Utils.GetSystemHosts(); var systemHostsMap = Utils.GetSystemHosts();

View file

@ -333,9 +333,14 @@ public partial class CoreConfigV2rayService
} }
} }
foreach (var kvp in Utils.ParseHostsToDictionary(simpleDNSItem.Hosts)) if (!simpleDNSItem.Hosts.IsNullOrEmpty())
{ {
dnsItem.hosts[kvp.Key] = kvp.Value; var userHostsMap = Utils.ParseHostsToDictionary(simpleDNSItem.Hosts);
foreach (var kvp in userHostsMap)
{
dnsItem.hosts[kvp.Key] = kvp.Value;
}
} }
return await Task.FromResult(0); return await Task.FromResult(0);
} }

View file

@ -97,12 +97,12 @@ public partial class CoreConfigV2rayService
serversItem.method = null; serversItem.method = null;
serversItem.password = null; serversItem.password = null;
if (node.Username.IsNotEmpty() if (protocolExtra.Username.IsNotEmpty()
&& node.Password.IsNotEmpty()) && node.Password.IsNotEmpty())
{ {
SocksUsersItem4Ray socksUsersItem = new() SocksUsersItem4Ray socksUsersItem = new()
{ {
user = node.Username ?? "", user = protocolExtra.Username ?? "",
pass = node.Password, pass = node.Password,
level = 1 level = 1
}; };
@ -144,12 +144,13 @@ public partial class CoreConfigV2rayService
usersItem.email = Global.UserEMail; usersItem.email = Global.UserEMail;
usersItem.encryption = protocolExtra.VlessEncryption; usersItem.encryption = protocolExtra.VlessEncryption;
if (!protocolExtra.Flow.IsNullOrEmpty()) if (protocolExtra.Flow.IsNullOrEmpty())
{ {
usersItem.flow = protocolExtra.Flow; await GenOutboundMux(node, outbound, muxEnabled, muxEnabled);
} }
else else
{ {
usersItem.flow = protocolExtra.Flow;
await GenOutboundMux(node, outbound, false, muxEnabled); await GenOutboundMux(node, outbound, false, muxEnabled);
} }
outbound.settings.servers = null; outbound.settings.servers = null;
@ -514,15 +515,13 @@ public partial class CoreConfigV2rayService
var ports = protocolExtra?.Ports; var ports = protocolExtra?.Ports;
int? upMbps = protocolExtra?.UpMbps is { } su and >= 0 int? upMbps = protocolExtra?.UpMbps is { } su and >= 0
? su ? su
: _config.HysteriaItem.UpMbps; : 0;
int? downMbps = protocolExtra?.DownMbps is { } sd and >= 0 int? downMbps = protocolExtra?.DownMbps is { } sd and >= 0
? sd ? sd
: _config.HysteriaItem.UpMbps; : 0;
var hopInterval = !protocolExtra.HopInterval.IsNullOrEmpty() var hopInterval = protocolExtra?.HopInterval is { } hi and >= 5
? protocolExtra.HopInterval ? hi
: (_config.HysteriaItem.HopInterval >= 5 : _config.HysteriaItem.HopInterval >= 5 ? _config.HysteriaItem.HopInterval : Global.Hysteria2DefaultHopInt;
? _config.HysteriaItem.HopInterval
: Global.Hysteria2DefaultHopInt).ToString();
HysteriaUdpHop4Ray? udpHop = null; HysteriaUdpHop4Ray? udpHop = null;
if (!ports.IsNullOrEmpty() && if (!ports.IsNullOrEmpty() &&
(ports.Contains(':') || ports.Contains('-') || ports.Contains(','))) (ports.Contains(':') || ports.Contains('-') || ports.Contains(',')))

View file

@ -33,7 +33,7 @@ public class AddServerViewModel : MyReactiveObject
public int DownMbps { get; set; } public int DownMbps { get; set; }
[Reactive] [Reactive]
public string HopInterval { get; set; } public int HopInterval { get; set; }
[Reactive] [Reactive]
public string Flow { get; set; } public string Flow { get; set; }
@ -47,6 +47,9 @@ public class AddServerViewModel : MyReactiveObject
[Reactive] [Reactive]
public string SsMethod { get; set; } public string SsMethod { get; set; }
[Reactive]
public string Username { get; set; }
[Reactive] [Reactive]
public string WgPublicKey { get; set; } public string WgPublicKey { get; set; }
//[Reactive] //[Reactive]
@ -110,12 +113,13 @@ public class AddServerViewModel : MyReactiveObject
AlterId = int.TryParse(protocolExtra?.AlterId, out var result) ? result : 0; AlterId = int.TryParse(protocolExtra?.AlterId, out var result) ? result : 0;
Flow = protocolExtra?.Flow ?? string.Empty; Flow = protocolExtra?.Flow ?? string.Empty;
SalamanderPass = protocolExtra?.SalamanderPass ?? string.Empty; SalamanderPass = protocolExtra?.SalamanderPass ?? string.Empty;
UpMbps = protocolExtra?.UpMbps ?? _config.HysteriaItem.UpMbps; UpMbps = protocolExtra?.UpMbps ?? 0;
DownMbps = protocolExtra?.DownMbps ?? _config.HysteriaItem.DownMbps; DownMbps = protocolExtra?.DownMbps ?? 0;
HopInterval = protocolExtra?.HopInterval.IsNullOrEmpty() ?? true ? Global.Hysteria2DefaultHopInt.ToString() : protocolExtra.HopInterval; HopInterval = protocolExtra?.HopInterval ?? Global.Hysteria2DefaultHopInt;
VmessSecurity = protocolExtra?.VmessSecurity?.IsNullOrEmpty() == false ? protocolExtra.VmessSecurity : Global.DefaultSecurity; VmessSecurity = protocolExtra?.VmessSecurity?.IsNullOrEmpty() == false ? protocolExtra.VmessSecurity : Global.DefaultSecurity;
VlessEncryption = protocolExtra?.VlessEncryption.IsNullOrEmpty() == false ? protocolExtra.VlessEncryption : Global.None; VlessEncryption = protocolExtra?.VlessEncryption.IsNullOrEmpty() == false ? protocolExtra.VlessEncryption : Global.None;
SsMethod = protocolExtra?.SsMethod ?? string.Empty; SsMethod = protocolExtra?.SsMethod ?? string.Empty;
Username = protocolExtra?.Username ?? string.Empty;
WgPublicKey = protocolExtra?.WgPublicKey ?? string.Empty; WgPublicKey = protocolExtra?.WgPublicKey ?? string.Empty;
WgInterfaceAddress = protocolExtra?.WgInterfaceAddress ?? string.Empty; WgInterfaceAddress = protocolExtra?.WgInterfaceAddress ?? string.Empty;
WgReserved = protocolExtra?.WgReserved ?? string.Empty; WgReserved = protocolExtra?.WgReserved ?? string.Empty;
@ -172,12 +176,13 @@ public class AddServerViewModel : MyReactiveObject
AlterId = AlterId > 0 ? AlterId.ToString() : null, AlterId = AlterId > 0 ? AlterId.ToString() : null,
Flow = Flow.NullIfEmpty(), Flow = Flow.NullIfEmpty(),
SalamanderPass = SalamanderPass.NullIfEmpty(), SalamanderPass = SalamanderPass.NullIfEmpty(),
UpMbps = UpMbps >= 0 ? UpMbps : null, UpMbps = UpMbps > 0 ? UpMbps : null,
DownMbps = DownMbps >= 0 ? DownMbps : null, DownMbps = DownMbps > 0 ? DownMbps : null,
HopInterval = HopInterval.NullIfEmpty(), HopInterval = HopInterval >= 5 ? HopInterval : null,
VmessSecurity = VmessSecurity.NullIfEmpty(), VmessSecurity = VmessSecurity.NullIfEmpty(),
VlessEncryption = VlessEncryption.NullIfEmpty(), VlessEncryption = VlessEncryption.NullIfEmpty(),
SsMethod = SsMethod.NullIfEmpty(), SsMethod = SsMethod.NullIfEmpty(),
Username = Username.NullIfEmpty(),
WgPublicKey = WgPublicKey.NullIfEmpty(), WgPublicKey = WgPublicKey.NullIfEmpty(),
WgInterfaceAddress = WgInterfaceAddress.NullIfEmpty(), WgInterfaceAddress = WgInterfaceAddress.NullIfEmpty(),
WgReserved = WgReserved.NullIfEmpty(), WgReserved = WgReserved.NullIfEmpty(),

View file

@ -1,3 +1,4 @@
using Avalonia.Controls;
using v2rayN.Desktop.Base; using v2rayN.Desktop.Base;
namespace v2rayN.Desktop.Views; namespace v2rayN.Desktop.Views;

View file

@ -126,7 +126,7 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
case EConfigType.SOCKS: case EConfigType.SOCKS:
case EConfigType.HTTP: case EConfigType.HTTP:
this.Bind(ViewModel, vm => vm.SelectedSource.Password, 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.Username, v => v.txtSecurity4.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.Username, v => v.txtSecurity4.Text).DisposeWith(disposables);
break; break;
case EConfigType.VLESS: case EConfigType.VLESS:
@ -152,7 +152,7 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
break; break;
case EConfigType.TUIC: case EConfigType.TUIC:
this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtId8.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.Username, v => v.txtId8.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Password, 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;

View file

@ -1,3 +1,5 @@
using System.Windows.Controls;
namespace v2rayN.Views; namespace v2rayN.Views;
public partial class AddGroupServerWindow public partial class AddGroupServerWindow
@ -156,11 +158,11 @@ public partial class AddGroupServerWindow
lstChild.SelectAll(); lstChild.SelectAll();
} }
private async void TabControl_SelectionChanged(object? sender, System.Windows.Controls.SelectionChangedEventArgs e) private async void TabControl_SelectionChanged(object? sender, SelectionChangedEventArgs e)
{ {
try try
{ {
if (e.Source is not System.Windows.Controls.TabControl tc) if (e.Source is not TabControl tc)
{ {
return; return;
} }

View file

@ -121,7 +121,7 @@ public partial class AddServerWindow
case EConfigType.SOCKS: case EConfigType.SOCKS:
case EConfigType.HTTP: case EConfigType.HTTP:
this.Bind(ViewModel, vm => vm.SelectedSource.Password, 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.Username, v => v.txtSecurity4.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.Username, v => v.txtSecurity4.Text).DisposeWith(disposables);
break; break;
case EConfigType.VLESS: case EConfigType.VLESS:
@ -147,7 +147,7 @@ public partial class AddServerWindow
break; break;
case EConfigType.TUIC: case EConfigType.TUIC:
this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtId8.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.Username, v => v.txtId8.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Password, 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;