Code clean
Some checks failed
release Linux / build (push) Has been cancelled
release Linux / build and release deb x64 & arm64 (push) Has been cancelled
release Linux / build and release rpm x64 & arm64 (push) Has been cancelled
release Linux / build and release rpm riscv64 (push) Has been cancelled
release macOS / build (push) Has been cancelled
release Windows desktop (Avalonia UI) / build (push) Has been cancelled
release Windows / build (push) Has been cancelled
release Linux / release-zip (push) Has been cancelled
release macOS / release-zip (push) Has been cancelled
release macOS / package and release macOS dmg (push) Has been cancelled
release Windows desktop (Avalonia UI) / release-zip (push) Has been cancelled
release Windows / release-zip (push) Has been cancelled

This commit is contained in:
2dust 2026-04-26 19:24:57 +08:00
parent ae662a628d
commit 05e349e45c
18 changed files with 148 additions and 99 deletions

View file

@ -9,105 +9,105 @@ namespace ServiceLib.Tests.CoreConfig.Context;
public class CoreConfigContextBuilderTests public class CoreConfigContextBuilderTests
{ {
[Fact] [Fact]
public async Task ResolveNodeAsync_DirectCycleDependency_ShouldFailWithCycleError() public async Task ResolveNodeAsync_DirectCycleDependency_ShouldFailWithCycleError()
{ {
var config = CoreConfigTestFactory.CreateConfig(); var config = CoreConfigTestFactory.CreateConfig();
CoreConfigTestFactory.BindAppManagerConfig(config); CoreConfigTestFactory.BindAppManagerConfig(config);
var groupAId = NewId("group-a"); var groupAId = NewId("group-a");
var groupBId = NewId("group-b"); var groupBId = NewId("group-b");
var groupA = CoreConfigTestFactory.CreatePolicyGroupNode(ECoreType.Xray, groupAId, "group-a", [groupBId]); var groupA = CoreConfigTestFactory.CreatePolicyGroupNode(ECoreType.Xray, groupAId, "group-a", [groupBId]);
var groupB = CoreConfigTestFactory.CreatePolicyGroupNode(ECoreType.Xray, groupBId, "group-b", [groupAId]); var groupB = CoreConfigTestFactory.CreatePolicyGroupNode(ECoreType.Xray, groupBId, "group-b", [groupAId]);
await UpsertProfilesAsync(groupA, groupB); await UpsertProfilesAsync(groupA, groupB);
var context = CoreConfigTestFactory.CreateContext(config, groupA, ECoreType.Xray); var context = CoreConfigTestFactory.CreateContext(config, groupA, ECoreType.Xray);
context.AllProxiesMap.Clear(); context.AllProxiesMap.Clear();
var (_, validatorResult) = await CoreConfigContextBuilder.ResolveNodeAsync(context, groupA, false); var (_, validatorResult) = await CoreConfigContextBuilder.ResolveNodeAsync(context, groupA, false);
validatorResult.Success.Should().BeFalse(); validatorResult.Success.Should().BeFalse();
validatorResult.Errors.Should().Contain(msg => ContainsCycleDependencyMessage(msg)); validatorResult.Errors.Should().Contain(msg => ContainsCycleDependencyMessage(msg));
context.AllProxiesMap.Should().NotContainKey(groupA.IndexId); context.AllProxiesMap.Should().NotContainKey(groupA.IndexId);
context.AllProxiesMap.Should().NotContainKey(groupB.IndexId); context.AllProxiesMap.Should().NotContainKey(groupB.IndexId);
} }
[Fact] [Fact]
public async Task ResolveNodeAsync_IndirectCycleDependency_ShouldFailWithCycleError() public async Task ResolveNodeAsync_IndirectCycleDependency_ShouldFailWithCycleError()
{ {
var config = CoreConfigTestFactory.CreateConfig(); var config = CoreConfigTestFactory.CreateConfig();
CoreConfigTestFactory.BindAppManagerConfig(config); CoreConfigTestFactory.BindAppManagerConfig(config);
var groupAId = NewId("group-a"); var groupAId = NewId("group-a");
var groupBId = NewId("group-b"); var groupBId = NewId("group-b");
var groupCId = NewId("group-c"); var groupCId = NewId("group-c");
var groupA = CoreConfigTestFactory.CreatePolicyGroupNode(ECoreType.Xray, groupAId, "group-a", [groupBId]); var groupA = CoreConfigTestFactory.CreatePolicyGroupNode(ECoreType.Xray, groupAId, "group-a", [groupBId]);
var groupB = CoreConfigTestFactory.CreatePolicyGroupNode(ECoreType.Xray, groupBId, "group-b", [groupCId]); var groupB = CoreConfigTestFactory.CreatePolicyGroupNode(ECoreType.Xray, groupBId, "group-b", [groupCId]);
var groupC = CoreConfigTestFactory.CreatePolicyGroupNode(ECoreType.Xray, groupCId, "group-c", [groupAId]); var groupC = CoreConfigTestFactory.CreatePolicyGroupNode(ECoreType.Xray, groupCId, "group-c", [groupAId]);
await UpsertProfilesAsync(groupA, groupB, groupC); await UpsertProfilesAsync(groupA, groupB, groupC);
var context = CoreConfigTestFactory.CreateContext(config, groupA, ECoreType.Xray); var context = CoreConfigTestFactory.CreateContext(config, groupA, ECoreType.Xray);
context.AllProxiesMap.Clear(); context.AllProxiesMap.Clear();
var (_, validatorResult) = await CoreConfigContextBuilder.ResolveNodeAsync(context, groupA, false); var (_, validatorResult) = await CoreConfigContextBuilder.ResolveNodeAsync(context, groupA, false);
validatorResult.Success.Should().BeFalse(); validatorResult.Success.Should().BeFalse();
validatorResult.Errors.Should().Contain(msg => ContainsCycleDependencyMessage(msg)); validatorResult.Errors.Should().Contain(msg => ContainsCycleDependencyMessage(msg));
context.AllProxiesMap.Should().NotContainKey(groupA.IndexId); context.AllProxiesMap.Should().NotContainKey(groupA.IndexId);
context.AllProxiesMap.Should().NotContainKey(groupB.IndexId); context.AllProxiesMap.Should().NotContainKey(groupB.IndexId);
context.AllProxiesMap.Should().NotContainKey(groupC.IndexId); context.AllProxiesMap.Should().NotContainKey(groupC.IndexId);
} }
[Fact] [Fact]
public async Task ResolveNodeAsync_CycleWithValidBranch_ShouldSkipCycleAndKeepValidChild() public async Task ResolveNodeAsync_CycleWithValidBranch_ShouldSkipCycleAndKeepValidChild()
{ {
var config = CoreConfigTestFactory.CreateConfig(); var config = CoreConfigTestFactory.CreateConfig();
CoreConfigTestFactory.BindAppManagerConfig(config); CoreConfigTestFactory.BindAppManagerConfig(config);
var groupAId = NewId("group-a"); var groupAId = NewId("group-a");
var groupBId = NewId("group-b"); var groupBId = NewId("group-b");
var leafId = NewId("leaf"); var leafId = NewId("leaf");
var groupA = CoreConfigTestFactory.CreatePolicyGroupNode(ECoreType.Xray, groupAId, "group-a", [groupBId, leafId]); var groupA = CoreConfigTestFactory.CreatePolicyGroupNode(ECoreType.Xray, groupAId, "group-a", [groupBId, leafId]);
var groupB = CoreConfigTestFactory.CreatePolicyGroupNode(ECoreType.Xray, groupBId, "group-b", [groupAId]); var groupB = CoreConfigTestFactory.CreatePolicyGroupNode(ECoreType.Xray, groupBId, "group-b", [groupAId]);
var leaf = CoreConfigTestFactory.CreateSocksNode(ECoreType.Xray, leafId, "leaf"); var leaf = CoreConfigTestFactory.CreateSocksNode(ECoreType.Xray, leafId, "leaf");
await UpsertProfilesAsync(groupA, groupB, leaf); await UpsertProfilesAsync(groupA, groupB, leaf);
var context = CoreConfigTestFactory.CreateContext(config, groupA, ECoreType.Xray); var context = CoreConfigTestFactory.CreateContext(config, groupA, ECoreType.Xray);
context.AllProxiesMap.Clear(); context.AllProxiesMap.Clear();
var (_, validatorResult) = await CoreConfigContextBuilder.ResolveNodeAsync(context, groupA, false); var (_, validatorResult) = await CoreConfigContextBuilder.ResolveNodeAsync(context, groupA, false);
validatorResult.Success.Should().BeTrue(); validatorResult.Success.Should().BeTrue();
validatorResult.Errors.Should().BeEmpty(); validatorResult.Errors.Should().BeEmpty();
validatorResult.Warnings.Should().Contain(msg => ContainsCycleDependencyMessage(msg)); validatorResult.Warnings.Should().Contain(msg => ContainsCycleDependencyMessage(msg));
context.AllProxiesMap.Should().ContainKey(leaf.IndexId); context.AllProxiesMap.Should().ContainKey(leaf.IndexId);
context.AllProxiesMap.Should().ContainKey(groupA.IndexId); context.AllProxiesMap.Should().ContainKey(groupA.IndexId);
context.AllProxiesMap.Should().NotContainKey(groupB.IndexId); context.AllProxiesMap.Should().NotContainKey(groupB.IndexId);
groupA.GetProtocolExtra().ChildItems.Should().Be(leaf.IndexId); groupA.GetProtocolExtra().ChildItems.Should().Be(leaf.IndexId);
} }
private static string NewId(string prefix) private static string NewId(string prefix)
{ {
return $"{prefix}-{Guid.NewGuid():N}"; return $"{prefix}-{Guid.NewGuid():N}";
} }
private static bool ContainsCycleDependencyMessage(string message) private static bool ContainsCycleDependencyMessage(string message)
{ {
return message.Contains("cycle dependency", StringComparison.OrdinalIgnoreCase) return message.Contains("cycle dependency", StringComparison.OrdinalIgnoreCase)
|| message.Contains("循环依赖", StringComparison.Ordinal) || message.Contains("循环依赖", StringComparison.Ordinal)
|| message.Contains("循環依賴", StringComparison.Ordinal); || message.Contains("循環依賴", StringComparison.Ordinal);
} }
private static async Task UpsertProfilesAsync(params ProfileItem[] profiles) private static async Task UpsertProfilesAsync(params ProfileItem[] profiles)
{ {
SQLiteHelper.Instance.CreateTable<ProfileItem>(); SQLiteHelper.Instance.CreateTable<ProfileItem>();
foreach (var profile in profiles) foreach (var profile in profiles)
{ {
await SQLiteHelper.Instance.ReplaceAsync(profile); await SQLiteHelper.Instance.ReplaceAsync(profile);
} }
} }
} }

View file

@ -1,7 +1,7 @@
using System.Reflection;
using ServiceLib.Enums; using ServiceLib.Enums;
using ServiceLib.Manager; using ServiceLib.Manager;
using ServiceLib.Models; using ServiceLib.Models;
using System.Reflection;
namespace ServiceLib.Tests.CoreConfig; namespace ServiceLib.Tests.CoreConfig;
@ -33,7 +33,10 @@ internal static class CoreConfigTestFactory
UiItem = UiItem =
new UIItem new UIItem
{ {
CurrentLanguage = "en", CurrentFontFamily = "sans", MainColumnItem = [], WindowSizeItem = [] CurrentLanguage = "en",
CurrentFontFamily = "sans",
MainColumnItem = [],
WindowSizeItem = []
}, },
ConstItem = new ConstItem(), ConstItem = new ConstItem(),
SpeedTestItem = new SpeedTestItem SpeedTestItem = new SpeedTestItem
@ -51,7 +54,8 @@ internal static class CoreConfigTestFactory
SystemProxyItem = SystemProxyItem =
new SystemProxyItem new SystemProxyItem
{ {
SystemProxyExceptions = string.Empty, SystemProxyAdvancedProtocol = string.Empty SystemProxyExceptions = string.Empty,
SystemProxyAdvancedProtocol = string.Empty
}, },
WebDavItem = new WebDavItem(), WebDavItem = new WebDavItem(),
CheckUpdateItem = new CheckUpdateItem(), CheckUpdateItem = new CheckUpdateItem(),
@ -131,11 +135,15 @@ internal static class CoreConfigTestFactory
{ {
var node = new ProfileItem var node = new ProfileItem
{ {
IndexId = indexId, ConfigType = EConfigType.PolicyGroup, CoreType = coreType, Remarks = remarks, IndexId = indexId,
ConfigType = EConfigType.PolicyGroup,
CoreType = coreType,
Remarks = remarks,
}; };
node.SetProtocolExtra(node.GetProtocolExtra() with node.SetProtocolExtra(node.GetProtocolExtra() with
{ {
GroupType = nameof(EConfigType.PolicyGroup), ChildItems = string.Join(",", childIndexIds), GroupType = nameof(EConfigType.PolicyGroup),
ChildItems = string.Join(",", childIndexIds),
}); });
return node; return node;
@ -146,11 +154,15 @@ internal static class CoreConfigTestFactory
{ {
var node = new ProfileItem var node = new ProfileItem
{ {
IndexId = indexId, ConfigType = EConfigType.ProxyChain, CoreType = coreType, Remarks = remarks, IndexId = indexId,
ConfigType = EConfigType.ProxyChain,
CoreType = coreType,
Remarks = remarks,
}; };
node.SetProtocolExtra(node.GetProtocolExtra() with node.SetProtocolExtra(node.GetProtocolExtra() with
{ {
GroupType = nameof(EConfigType.ProxyChain), ChildItems = string.Join(",", childIndexIds), GroupType = nameof(EConfigType.ProxyChain),
ChildItems = string.Join(",", childIndexIds),
}); });
return node; return node;

View file

@ -1,7 +1,7 @@
using AwesomeAssertions; using AwesomeAssertions;
using ServiceLib.Enums;
using ServiceLib.Handler.Fmt; using ServiceLib.Handler.Fmt;
using ServiceLib.Models; using ServiceLib.Models;
using ServiceLib.Enums;
using Xunit; using Xunit;
namespace ServiceLib.Tests.Fmt; namespace ServiceLib.Tests.Fmt;
@ -92,7 +92,7 @@ public class FmtHandlerTests
var uri = FmtHandler.GetShareUri(source); var uri = FmtHandler.GetShareUri(source);
uri.Should().NotBeNullOrWhiteSpace(); uri.Should().NotBeNullOrWhiteSpace();
(uri!.StartsWith(Global.ProtocolShares[source.ConfigType], StringComparison.OrdinalIgnoreCase)).Should() uri!.StartsWith(Global.ProtocolShares[source.ConfigType], StringComparison.OrdinalIgnoreCase).Should()
.BeTrue(); .BeTrue();
var resolved = FmtHandler.ResolveConfig(uri, out var msg); var resolved = FmtHandler.ResolveConfig(uri, out var msg);

View file

@ -239,7 +239,9 @@ public class Socks5UdpChannel(string socks5Host, int socks5TcpPort) : IDisposabl
var clientAddrForSocks = new Socks5AddressData var clientAddrForSocks = new Socks5AddressData
{ {
AddressType = Socks5AddressData.AddrTypeIPv4, Host = "0.0.0.0", Port = 0 AddressType = Socks5AddressData.AddrTypeIPv4,
Host = "0.0.0.0",
Port = 0
}; };
using var udpAssociateReqMs = new MemoryStream(); using var udpAssociateReqMs = new MemoryStream();
udpAssociateReqMs.WriteByte(Socks5Version); udpAssociateReqMs.WriteByte(Socks5Version);
@ -267,7 +269,7 @@ public class Socks5UdpChannel(string socks5Host, int socks5TcpPort) : IDisposabl
return true; return true;
} }
#endregion #endregion SOCKS5 Connection Handling
#region SOCKS5 Address Handling #region SOCKS5 Address Handling
@ -298,6 +300,7 @@ public class Socks5UdpChannel(string socks5Host, int socks5TcpPort) : IDisposabl
} }
break; break;
case AddrTypeDomain: case AddrTypeDomain:
if (string.IsNullOrEmpty(Host)) if (string.IsNullOrEmpty(Host))
{ {
@ -311,6 +314,7 @@ public class Socks5UdpChannel(string socks5Host, int socks5TcpPort) : IDisposabl
} }
break; break;
case AddrTypeIPv6: case AddrTypeIPv6:
if (IPAddress.TryParse(Host, out var ip6) && ip6.AddressFamily == AddressFamily.InterNetworkV6) if (IPAddress.TryParse(Host, out var ip6) && ip6.AddressFamily == AddressFamily.InterNetworkV6)
{ {
@ -322,6 +326,7 @@ public class Socks5UdpChannel(string socks5Host, int socks5TcpPort) : IDisposabl
} }
break; break;
default: default:
throw new NotSupportedException($"SOCKS5 address type {AddressType} not supported."); throw new NotSupportedException($"SOCKS5 address type {AddressType} not supported.");
} }
@ -355,6 +360,7 @@ public class Socks5UdpChannel(string socks5Host, int socks5TcpPort) : IDisposabl
addr.Host = new IPAddress(ipv4Bytes).ToString(); addr.Host = new IPAddress(ipv4Bytes).ToString();
break; break;
case AddrTypeDomain: case AddrTypeDomain:
var lenByte = new byte[1]; var lenByte = new byte[1];
if (await stream.ReadAsync(lenByte.AsMemory(0, 1), ct).ConfigureAwait(false) < 1) if (await stream.ReadAsync(lenByte.AsMemory(0, 1), ct).ConfigureAwait(false) < 1)
@ -379,6 +385,7 @@ public class Socks5UdpChannel(string socks5Host, int socks5TcpPort) : IDisposabl
} }
break; break;
case AddrTypeIPv6: case AddrTypeIPv6:
var ipv6Bytes = new byte[16]; var ipv6Bytes = new byte[16];
if (await stream.ReadAsync(ipv6Bytes.AsMemory(0, 16), ct).ConfigureAwait(false) < 16) if (await stream.ReadAsync(ipv6Bytes.AsMemory(0, 16), ct).ConfigureAwait(false) < 16)
@ -388,6 +395,7 @@ public class Socks5UdpChannel(string socks5Host, int socks5TcpPort) : IDisposabl
addr.Host = new IPAddress(ipv6Bytes).ToString(); addr.Host = new IPAddress(ipv6Bytes).ToString();
break; break;
default: default:
return null; return null;
} }

View file

@ -4,6 +4,7 @@ public class DnsService : IUdpTest
{ {
private const int DnsDefaultPort = 53; private const int DnsDefaultPort = 53;
private const string DnsDefaultServer = "8.8.8.8"; // Google Public DNS private const string DnsDefaultServer = "8.8.8.8"; // Google Public DNS
private static readonly byte[] DnsQueryPacket = private static readonly byte[] DnsQueryPacket =
[ [
// Header: ID=0x1234, Standard query with RD set, QDCOUNT=1 // Header: ID=0x1234, Standard query with RD set, QDCOUNT=1

View file

@ -3,7 +3,10 @@ namespace ServiceLib.UdpTest.Tester;
public interface IUdpTest public interface IUdpTest
{ {
public byte[] BuildUdpRequestPacket(); public byte[] BuildUdpRequestPacket();
public bool VerifyAndExtractUdpResponse(byte[] udpResponseBytes); public bool VerifyAndExtractUdpResponse(byte[] udpResponseBytes);
public ushort GetDefaultTargetPort(); public ushort GetDefaultTargetPort();
public string GetDefaultTargetHost(); public string GetDefaultTargetHost();
} }

View file

@ -4,6 +4,7 @@ public class McBeService : IUdpTest
{ {
private const int McBeDefaultPort = 19132; private const int McBeDefaultPort = 19132;
private const string McBeDefaultServer = "pms.mc-complex.com"; private const string McBeDefaultServer = "pms.mc-complex.com";
// 0x01 | client alive time in ms (unsigned long long) | magic | client GUID // 0x01 | client alive time in ms (unsigned long long) | magic | client GUID
private static readonly byte[] McBeQueryPacket = private static readonly byte[] McBeQueryPacket =
[ [
@ -18,11 +19,13 @@ public class McBeService : IUdpTest
0x66, 0x0E, 0xAB, 0xBC, 0x61, 0x0D, 0x1F, 0x4E, 0x66, 0x0E, 0xAB, 0xBC, 0x61, 0x0D, 0x1F, 0x4E,
0xA4, 0x40, 0x8C, 0x65, 0xC1, 0xBE, 0xF5, 0x4B 0xA4, 0x40, 0x8C, 0x65, 0xC1, 0xBE, 0xF5, 0x4B
]; ];
private static readonly byte[] McBeMagicBytes = private static readonly byte[] McBeMagicBytes =
[ [
0x00, 0xFF, 0xFF, 0x00, 0xFE, 0xFE, 0xFE, 0xFE, 0x00, 0xFF, 0xFF, 0x00, 0xFE, 0xFE, 0xFE, 0xFE,
0xFD, 0xFD, 0xFD, 0xFD, 0x12, 0x34, 0x56, 0x78 0xFD, 0xFD, 0xFD, 0xFD, 0x12, 0x34, 0x56, 0x78
]; ];
private static readonly List<string> ValidGameModes = private static readonly List<string> ValidGameModes =
[ [
"Survival", "Survival",

View file

@ -4,6 +4,7 @@ public class StunService : IUdpTest
{ {
private const int StunDefaultPort = 3478; private const int StunDefaultPort = 3478;
private const string StunDefaultServer = "stun.voztovoice.org"; private const string StunDefaultServer = "stun.voztovoice.org";
private static readonly byte[] StunBindingRequestPacket = private static readonly byte[] StunBindingRequestPacket =
[ [
// STUN Binding Request // STUN Binding Request

View file

@ -5,6 +5,7 @@ namespace ServiceLib.Handler.Fmt;
public class BaseFmt public class BaseFmt
{ {
private static readonly string[] _allowInsecureArray = new[] { "insecure", "allowInsecure", "allow_insecure" }; private static readonly string[] _allowInsecureArray = new[] { "insecure", "allowInsecure", "allow_insecure" };
private static string UrlEncodeSafe(string? value) => Utils.UrlEncode(value ?? string.Empty); private static string UrlEncodeSafe(string? value) => Utils.UrlEncode(value ?? string.Empty);
protected static string GetIpv6(string address) protected static string GetIpv6(string address)

View file

@ -237,6 +237,7 @@ public class Transport4Sbox
public class Headers4Sbox public class Headers4Sbox
{ {
public string? Host { get; set; } public string? Host { get; set; }
[JsonPropertyName("User-Agent")] [JsonPropertyName("User-Agent")]
public string UserAgent { get; set; } public string UserAgent { get; set; }
} }

View file

@ -500,12 +500,16 @@ public class MaskSettings4Ray
{ {
public string? password { get; set; } public string? password { get; set; }
public string? domain { get; set; } public string? domain { get; set; }
// fragment // fragment
public string? packets { get; set; } public string? packets { get; set; }
public string? length { get; set; } public string? length { get; set; }
public string? delay { get; set; } public string? delay { get; set; }
// noise // noise
public int? reset { get; set; } public int? reset { get; set; }
public List<NoiseMask4Ray>? noise { get; set; } public List<NoiseMask4Ray>? noise { get; set; }
} }
@ -533,6 +537,7 @@ public class AccountsItem4Ray
public class Sockopt4Ray public class Sockopt4Ray
{ {
public string? dialerProxy { get; set; } public string? dialerProxy { get; set; }
[JsonPropertyName("interface")] [JsonPropertyName("interface")]
public string? Interface { get; set; } public string? Interface { get; set; }
} }

View file

@ -548,6 +548,7 @@ public partial class CoreConfigV2rayService
FillOutboundMux(outbound); FillOutboundMux(outbound);
break; break;
case nameof(ETransport.grpc): case nameof(ETransport.grpc):
GrpcSettings4Ray grpcSettings = new() GrpcSettings4Ray grpcSettings = new()
{ {

View file

@ -299,7 +299,6 @@ public class UpdateService(Config config, Func<bool, string, Task> updateFunc)
return url; return url;
} }
else if (Utils.IsLinux()) else if (Utils.IsLinux())
{ {
var arch = RuntimeInformation.ProcessArchitecture; var arch = RuntimeInformation.ProcessArchitecture;
@ -314,7 +313,6 @@ public class UpdateService(Config config, Func<bool, string, Task> updateFunc)
_ => null, _ => null,
}; };
} }
else if (Utils.IsMacOS()) else if (Utils.IsMacOS())
{ {
return RuntimeInformation.ProcessArchitecture switch return RuntimeInformation.ProcessArchitecture switch

View file

@ -338,21 +338,27 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
case nameof(ETransport.raw): case nameof(ETransport.raw):
gridTransportRaw.IsVisible = true; gridTransportRaw.IsVisible = true;
break; break;
case nameof(ETransport.kcp): case nameof(ETransport.kcp):
gridTransportKcp.IsVisible = true; gridTransportKcp.IsVisible = true;
break; break;
case nameof(ETransport.ws): case nameof(ETransport.ws):
gridTransportWs.IsVisible = true; gridTransportWs.IsVisible = true;
break; break;
case nameof(ETransport.httpupgrade): case nameof(ETransport.httpupgrade):
gridTransportHttpupgrade.IsVisible = true; gridTransportHttpupgrade.IsVisible = true;
break; break;
case nameof(ETransport.xhttp): case nameof(ETransport.xhttp):
gridTransportXhttp.IsVisible = true; gridTransportXhttp.IsVisible = true;
break; break;
case nameof(ETransport.grpc): case nameof(ETransport.grpc):
gridTransportGrpc.IsVisible = true; gridTransportGrpc.IsVisible = true;
break; break;
default: default:
gridTransportRaw.IsVisible = true; gridTransportRaw.IsVisible = true;
break; break;

View file

@ -64,7 +64,10 @@ public partial class RoutingSettingWindow : WindowBase<RoutingSettingViewModel>
private void RoutingSettingWindow_Closing(object? sender, WindowClosingEventArgs e) private void RoutingSettingWindow_Closing(object? sender, WindowClosingEventArgs e)
{ {
if (_closed) return; if (_closed)
{
return;
}
// DomainStrategy is auto-saved reactively; just ensure the caller knows changes were made // DomainStrategy is auto-saved reactively; just ensure the caller knows changes were made
if (ViewModel?.IsModified == true) if (ViewModel?.IsModified == true)

View file

@ -137,7 +137,7 @@ public class ThemeSettingViewModel : MyReactiveObject
private void ModifyFontSize() private void ModifyFontSize()
{ {
double size = (long)CurrentFontSize; double size = CurrentFontSize;
if (size < Global.MinFontSize) if (size < Global.MinFontSize)
{ {
return; return;

View file

@ -340,21 +340,27 @@ public partial class AddServerWindow
case nameof(ETransport.raw): case nameof(ETransport.raw):
gridTransportRaw.Visibility = Visibility.Visible; gridTransportRaw.Visibility = Visibility.Visible;
break; break;
case nameof(ETransport.kcp): case nameof(ETransport.kcp):
gridTransportKcp.Visibility = Visibility.Visible; gridTransportKcp.Visibility = Visibility.Visible;
break; break;
case nameof(ETransport.ws): case nameof(ETransport.ws):
gridTransportWs.Visibility = Visibility.Visible; gridTransportWs.Visibility = Visibility.Visible;
break; break;
case nameof(ETransport.httpupgrade): case nameof(ETransport.httpupgrade):
gridTransportHttpupgrade.Visibility = Visibility.Visible; gridTransportHttpupgrade.Visibility = Visibility.Visible;
break; break;
case nameof(ETransport.xhttp): case nameof(ETransport.xhttp):
gridTransportXhttp.Visibility = Visibility.Visible; gridTransportXhttp.Visibility = Visibility.Visible;
break; break;
case nameof(ETransport.grpc): case nameof(ETransport.grpc):
gridTransportGrpc.Visibility = Visibility.Visible; gridTransportGrpc.Visibility = Visibility.Visible;
break; break;
default: default:
gridTransportRaw.Visibility = Visibility.Visible; gridTransportRaw.Visibility = Visibility.Visible;
break; break;

View file

@ -170,10 +170,10 @@ public partial class MainWindow
private void OnProgramStarted(object state, bool timeout) private void OnProgramStarted(object state, bool timeout)
{ {
Application.Current?.Dispatcher.Invoke((Action)(() => Application.Current?.Dispatcher.Invoke(() =>
{ {
ShowHideWindow(true); ShowHideWindow(true);
})); });
} }
private async Task DelegateSnackMsg(string content) private async Task DelegateSnackMsg(string content)