Compare commits

..
3 Commits
Author SHA1 Message Date
Aigio Liu 96a14d69bd 🚧 Misc 2024-08-06 17:09:13 +08:00
Aigio Liu c79fba98d3 🚧 Misc 2024-07-04 17:51:15 +08:00
Aigio Liu 4160bb43c5 🚧 Misc 2024-06-29 00:57:23 +08:00
229 changed files with 1737 additions and 3274 deletions
+5 -5
View File
@@ -68,19 +68,19 @@ jobs:
run: dotnet --info
- name: Build BD.WTTS.Client.AppHost
run: dotnet build -c Release src\BD.WTTS.Client.AppHost\BD.WTTS.Client.AppHost.csproj --nologo -v q --property:WarningLevel=1 --property:DebugType=pdbonly
run: dotnet build -c Release src\BD.WTTS.Client.AppHost\BD.WTTS.Client.AppHost.csproj --nologo -v q --property:WarningLevel=1;DebugType=pdbonly
- name: Build BD.WTTS.Client.Plugins.Accelerator.ReverseProxy
run: dotnet build -c Release src\BD.WTTS.Client.Plugins.Accelerator.ReverseProxy\BD.WTTS.Client.Plugins.Accelerator.ReverseProxy.csproj --nologo -v q --property:WarningLevel=1 --property:DebugType=pdbonly
run: dotnet build -c Release src\BD.WTTS.Client.Plugins.Accelerator.ReverseProxy\BD.WTTS.Client.Plugins.Accelerator.ReverseProxy.csproj --nologo -v q --property:WarningLevel=1;DebugType=pdbonly
- name: Build BD.WTTS.Client.Avalonia.App
run: dotnet build -c Release src\BD.WTTS.Client.Avalonia.App\BD.WTTS.Client.Avalonia.App.csproj --nologo -v q --property:WarningLevel=1 --property:DebugType=pdbonly
run: dotnet build -c Release src\BD.WTTS.Client.Avalonia.App\BD.WTTS.Client.Avalonia.App.csproj --nologo -v q --property:WarningLevel=1;DebugType=pdbonly
- name: Build BD.WTTS.Client.Avalonia.Designer.HostApp
run: dotnet build -c Release src\BD.WTTS.Client.Avalonia.Designer.HostApp\BD.WTTS.Client.Avalonia.Designer.HostApp.csproj --nologo -v q --property:WarningLevel=1 --property:DebugType=pdbonly
run: dotnet build -c Release src\BD.WTTS.Client.Avalonia.Designer.HostApp\BD.WTTS.Client.Avalonia.Designer.HostApp.csproj --nologo -v q --property:WarningLevel=1;DebugType=pdbonly
- name: Test
run: dotnet test src/BD.WTTS.UnitTest/BD.WTTS.UnitTest.csproj -c Release -p:GeneratePackageOnBuild=false --nologo -v q --property:WarningLevel=1 --property:DebugType=pdbonly
run: dotnet test src/BD.WTTS.UnitTest/BD.WTTS.UnitTest.csproj -c Release -p:GeneratePackageOnBuild=false --nologo -v q --property:WarningLevel=1;DebugType=pdbonly
# release:
# name: Release
+2 -2
View File
@@ -964,8 +964,8 @@
},
{
"TypeName": "bool",
"PropertyName": "UseDoh2",
"DefaultValue": "false",
"PropertyName": "UseDoh",
"DefaultValue": "true",
"DefaultValueIsConst": true,
"Summary": "启用 DNS over HTTPS",
"IsRegionOrEndregion": null,
+25 -1
View File
@@ -3,6 +3,14 @@ namespace BD.WTTS;
/// <summary>
/// 提供用来获取应用程序信息(如版本号、说明、加载的程序集等)的属性。
/// </summary>
[Mobius3(
"""
Mobius.AssemblyInfo
AssemblyInfo.cs
AssemblyInfo.Version.cs
AssemblyInfo.Version.NativeHost.cs
AssemblyInfo.Version.Setup.Windows.cs
""")]
public static partial class AssemblyInfo
{
const string Version2 = "3.0";
@@ -21,7 +29,7 @@ public static partial class AssemblyInfo
/// <summary>
/// RC 版本号,范围 0~9,只增不减
/// </summary>
const string ver_for_rc = "9";
const string ver_for_rc = "8";
public const string FileVersion = $"{Version2}.{ver_for_rc}0{ver_for_preview}.0";
@@ -146,4 +154,20 @@ false
#endif
}
[AttributeUsage(AttributeTargets.All, Inherited = false)]
sealed class Mobius3Attribute : Attribute
{
public Mobius3Attribute()
{
}
public Mobius3Attribute(string str)
{
}
public bool Obsolete { get; set; }
}
+1
View File
@@ -1,3 +1,4 @@
// Mobius Properties\XmlnsDefinition.cs
#if (WINDOWS || MACCATALYST || MACOS || LINUX) && !(IOS || ANDROID)
// C# 10 定义全局 using
File diff suppressed because it is too large Load Diff
@@ -11,7 +11,7 @@
<Identity
Name="4651ED44255E.47979655102CE"
Publisher="CN=A90E406B-B2D3-4A23-B061-0FA1D65C4F66"
Version="3.0.207.0" />
Version="3.0.999.0" />
<Properties>
<DisplayName>Watt Toolkit</DisplayName>
@@ -66,15 +66,6 @@
</Application>
</Applications>
<Extensions>
<desktop2:Extension Category="windows.firewallRules">
<desktop2:FirewallRules Executable="Steam++\modules\Accelerator\Steam++.Accelerator.exe">
<desktop2:Rule Direction="in" IPProtocol="TCP" Profile="all"/>
<desktop2:Rule Direction="in" IPProtocol="UDP" Profile="all"/>
</desktop2:FirewallRules>
</desktop2:Extension>
</Extensions>
<Capabilities>
<Capability Name="internetClient" />
<rescap:Capability Name="runFullTrust" />
@@ -13,7 +13,6 @@ public static class WindowTransparencyLevelExtensions
WindowBackgroundMaterial.Blur => WindowTransparencyLevel.Blur,
WindowBackgroundMaterial.AcrylicBlur => WindowTransparencyLevel.AcrylicBlur,
WindowBackgroundMaterial.Mica => WindowTransparencyLevel.Mica,
WindowBackgroundMaterial.Transparent => WindowTransparencyLevel.Transparent,
_ => WindowTransparencyLevel.None,
};
}
@@ -7,12 +7,28 @@ namespace BD.WTTS.UI;
partial class App
{
[Mobius(
"""
App.DefaultActualTheme
""")]
const AppTheme _DefaultActualTheme = AppTheme.FollowingSystem;
[Mobius(
"""
App.DefaultActualTheme
""")]
AppTheme IApplication.DefaultActualTheme => _DefaultActualTheme;
[Mobius(
"""
App.mTheme
""")]
AppTheme mTheme = _DefaultActualTheme;
[Mobius(
"""
App.Theme
""")]
public AppTheme Theme
{
get => mTheme;
@@ -50,6 +66,10 @@ partial class App
}
}
[Mobius(
"""
App.SetThemeNotChangeValue
""")]
public void SetThemeNotChangeValue(AppTheme value)
{
var mode = value switch
@@ -71,6 +91,10 @@ partial class App
}
}
[Mobius(
"""
App.SetThemeAccent
""")]
public static void SetThemeAccent(string? colorHex)
{
if (colorHex == null)
@@ -9,14 +9,34 @@ namespace BD.WTTS.UI;
partial class App
{
[Mobius(
"""
App.TrayMenus
""")]
public Dictionary<string, TrayMenuItem> TrayMenus { get; } = new Dictionary<string, TrayMenuItem>();
[Mobius(
"""
App.TrayIcons
""")]
public TrayIcons TrayIcons { get; init; } = new TrayIcons();
[Mobius(
"""
App.trayIconMenus
""")]
readonly Dictionary<string, ICommand> trayIconMenus = new();
[Mobius(
"""
App.TrayIconMenus
""")]
public IReadOnlyDictionary<string, ICommand> TrayIconMenus => trayIconMenus;
[Mobius(
"""
App.InitTrayIcon
""")]
void InitTrayIcon()
{
if (ApplicationLifetime is not
@@ -67,6 +87,10 @@ partial class App
}
}
[Mobius(
"""
App.UpdateMenuItems
""")]
public void UpdateMenuItems()
{
MainThread2.BeginInvokeOnMainThread(() =>
@@ -166,6 +190,10 @@ partial class App
});
}
[Mobius(
"""
App.UpdateMenuItems
""")]
public void UpdateMenuItems(string menuKey, TrayMenuItem trayMenuItem)
{
if (TrayMenus != null)
@@ -135,11 +135,11 @@ public class ReactiveAppWindow<TViewModel> : AppWindow, IViewFor<TViewModel>, IV
if (SizePosition.Height > 0 &&
primaryScreenBounds.Height >= SizePosition.Height)
Height = SizePosition.Height - 31; // appwindow偏移量
Height = SizePosition.Height;
if (SizePosition.X > 0 && SizePosition.Y > 0)
{
var leftTopPoint = new PixelPoint(SizePosition.X, SizePosition.Y + 31);
var leftTopPoint = new PixelPoint(SizePosition.X, SizePosition.Y);
var rightBottomPoint = new PixelPoint(SizePosition.X + (int)Width, SizePosition.Y + (int)Height);
if (primaryScreenBounds.Contains(leftTopPoint) &&
primaryScreenBounds.Contains(rightBottomPoint))
@@ -74,18 +74,13 @@
MaxWidth="1080"
Spacing="20">
<spp:Snackbar
Title="{Binding Path=Res.Notice_Tray_Title, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
VerticalAlignment="Top"
IsClosable="False"
IsOpen="True"
IsVisible="{spp:OnPlatform !GA}"
Message="当前使用的程序版本为测试版本,可能会有各种各样不可言明的 Bug,遇到问题请理性反馈,谢谢。"
Severity="Warning" />
<spp:Snackbar
VerticalAlignment="Top"
IsClosable="False"
IsOpen="True"
IsVisible="{spp:OnPlatform !GA}"
Message="如果您不喜欢当前主页为默认启动页面可以在【设置-界面设置-启动时默认打开页面】处更改" />
<spp:Snackbar
Title="{Binding Path=Res.Notice_Tray_Title, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
VerticalAlignment="Top"
@@ -218,7 +218,6 @@
<TextBlock Text="{Binding Path=User.NickName, Mode=OneWay, Source={x:Static s:UserService.Current}}" />
<Border
Padding="5,1"
HorizontalAlignment="Left"
VerticalAlignment="Center"
CornerRadius="2"
IsVisible="{Binding Path=User.UserType, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource EnumEqualValueConverter}, ConverterParameter={x:Static enum:UserType.Sponsor}}">
@@ -341,7 +341,6 @@
<ComboBox SelectedItem="{Binding Source={x:Static ms:UISettings.WindowBackgroundMaterial}, Path=Value, Mode=TwoWay}">
<ComboBox.Items>
<spp:WindowBackgroundMaterial>None</spp:WindowBackgroundMaterial>
<spp:WindowBackgroundMaterial>Transparent</spp:WindowBackgroundMaterial>
<spp:WindowBackgroundMaterial>Blur</spp:WindowBackgroundMaterial>
<spp:WindowBackgroundMaterial>AcrylicBlur</spp:WindowBackgroundMaterial>
<spp:WindowBackgroundMaterial>Mica</spp:WindowBackgroundMaterial>
@@ -62,7 +62,7 @@ public sealed partial class MainWindow : ReactiveAppWindow<MainWindowViewModel>
pageType = page.PageType;
}
}
INavigationService.Instance.Navigate(pageType ?? typeof(HomePage));
INavigationService.Instance.Navigate(pageType ?? typeof(PluginStorePage));
return true;
}
@@ -0,0 +1,17 @@
namespace BD;
[AttributeUsage(AttributeTargets.All, Inherited = false)]
public sealed class MobiusAttribute : Attribute
{
public MobiusAttribute()
{
}
public MobiusAttribute(string str)
{
}
public bool Obsolete { get; set; }
}
@@ -6,6 +6,7 @@ namespace BD.WTTS.Services;
/// <summary>
/// 子进程的 IPC 服务实现
/// </summary>
[Mobius(Obsolete = true)]
public interface IPCSubProcessService : IDisposable
{
/// <summary>
@@ -0,0 +1,17 @@
namespace BD;
[AttributeUsage(AttributeTargets.All, Inherited = false)]
public sealed class MobiusAttribute : Attribute
{
public MobiusAttribute()
{
}
public MobiusAttribute(string str)
{
}
public bool Obsolete { get; set; }
}
@@ -12,10 +12,10 @@ sealed class LifetimeHttpHandler : DelegatingHandler
public LifeTimeKey LifeTimeKey { get; }
public LifetimeHttpHandler(IDomainResolver domainResolver, IWebProxy webProxy, LifeTimeKey lifeTimeKey, TimeSpan lifeTime, Action<LifetimeHttpHandler> deactivateAction)
public LifetimeHttpHandler(IDomainResolver domainResolver, LifeTimeKey lifeTimeKey, TimeSpan lifeTime, Action<LifetimeHttpHandler> deactivateAction)
{
LifeTimeKey = lifeTimeKey;
InnerHandler = new ReverseProxyHttpClientHandler(lifeTimeKey.DomainConfig, domainResolver, webProxy);
InnerHandler = new ReverseProxyHttpClientHandler(lifeTimeKey.DomainConfig, domainResolver);
timer = new Timer(OnTimerCallback, deactivateAction, lifeTime, Timeout.InfiniteTimeSpan);
}
@@ -15,7 +15,7 @@ public class ReverseProxyHttpClient : HttpMessageInvoker
// = new(new ProductHeaderValue(Constants.HARDCODED_APP_NAME_NEW + "_" + DateTimeOffset.UtcNow.Ticks, ThisAssembly.Version));
public ReverseProxyHttpClient(IDomainConfig domainConfig, IDomainResolver domainResolver)
: this(new ReverseProxyHttpClientHandler(domainConfig, domainResolver, HttpNoProxy.Instance), disposeHandler: true)
: this(new ReverseProxyHttpClientHandler(domainConfig, domainResolver), disposeHandler: true)
{
}
@@ -7,14 +7,12 @@ sealed class ReverseProxyHttpClientHandler : DelegatingHandler
{
readonly IDomainConfig domainConfig;
readonly IDomainResolver domainResolver;
readonly IWebProxy webProxy;
readonly TimeSpan connectTimeout = TimeSpan.FromSeconds(10d);
public ReverseProxyHttpClientHandler(IDomainConfig domainConfig, IDomainResolver domainResolver, IWebProxy webProxy)
public ReverseProxyHttpClientHandler(IDomainConfig domainConfig, IDomainResolver domainResolver)
{
this.domainConfig = domainConfig;
this.domainResolver = domainResolver;
this.webProxy = webProxy;
InnerHandler = CreateSocketsHttpHandler();
}
@@ -55,199 +53,12 @@ sealed class ReverseProxyHttpClientHandler : DelegatingHandler
{
Proxy = HttpNoProxy.Instance,
UseProxy = false,
PreAuthenticate = false,
UseCookies = false,
AllowAutoRedirect = false,
AutomaticDecompression = DecompressionMethods.None,
ConnectCallback = ConnectCallback,
EnableMultipleHttp2Connections = true,
RequestHeaderEncodingSelector = (_, _) => Encoding.UTF8,
ResponseHeaderEncodingSelector = (_, _) => Encoding.UTF8,
};
private async ValueTask<Stream> ConnectThroughProxyAsync(SocketsHttpConnectionContext context, CancellationToken cancellationToken)
{
if (context.InitialRequestMessage.RequestUri == null)
{
throw new InvalidOperationException("Request URI is null");
}
var proxyUri = webProxy.GetProxy(context.InitialRequestMessage.RequestUri);
if (proxyUri == null)
{
throw new InvalidOperationException("Proxy URI is null");
}
var proxyEndPoint = new DnsEndPoint(proxyUri.Host, proxyUri.Port);
var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
await socket.ConnectAsync(proxyEndPoint, cancellationToken);
var stream = new NetworkStream(socket, ownsSocket: true);
// 确定代理类型
var proxyType = DetermineProxyType(proxyUri);
switch (proxyType)
{
case ExternalProxyType.Http:
await ConnectHttpProxy(stream, context, cancellationToken);
break;
case ExternalProxyType.Socks4:
await ConnectSocks4Proxy(stream, context, cancellationToken);
break;
case ExternalProxyType.Socks5:
await ConnectSocks5Proxy(stream, context, cancellationToken);
break;
}
if (context.InitialRequestMessage.RequestUri.Scheme == "https" || context.InitialRequestMessage.RequestUri.Port == 443)
{
var requestContext = context.InitialRequestMessage.GetRequestContext();
var tlsSniValue = requestContext.TlsSniValue;
var sslStream = new SslStream(stream, leaveInnerStreamOpen: false);
await sslStream.AuthenticateAsClientAsync(new SslClientAuthenticationOptions
{
TargetHost = tlsSniValue.Value,
RemoteCertificateValidationCallback = (sender, cert, chain, errors) => true,
}, cancellationToken);
return sslStream;
}
return stream;
}
private ExternalProxyType DetermineProxyType(Uri proxyUri)
{
return proxyUri.Scheme.ToLower() switch
{
"socks4" => ExternalProxyType.Socks4,
"socks5" => ExternalProxyType.Socks5,
_ => ExternalProxyType.Http
};
throw new NotImplementedException("Proxy type determination needs to be implemented");
}
private async Task ConnectHttpProxy(NetworkStream stream, SocketsHttpConnectionContext context, CancellationToken cancellationToken)
{
var credentials = webProxy.Credentials as NetworkCredential;
var authHeader = credentials != null
? $"Proxy-Authorization: Basic {Convert.ToBase64String(Encoding.ASCII.GetBytes($"{credentials.UserName}:{credentials.Password}"))}\r\n"
: string.Empty;
var connectRequest = $"CONNECT {context.DnsEndPoint.Host}:{context.DnsEndPoint.Port} HTTP/1.1\r\n" +
$"Host: {context.DnsEndPoint.Host}:{context.DnsEndPoint.Port}\r\n" +
$"{authHeader}\r\n";
var connectBytes = Encoding.ASCII.GetBytes(connectRequest);
await stream.WriteAsync(connectBytes, cancellationToken);
var buffer = new byte[1024];
var bytesRead = await stream.ReadAsync(buffer, cancellationToken);
var response = Encoding.ASCII.GetString(buffer, 0, bytesRead);
if (!response.StartsWith("HTTP/1.1 200"))
{
throw new Exception($"Proxy connection failed: {response}");
}
}
private async Task ConnectSocks4Proxy(NetworkStream stream, SocketsHttpConnectionContext context, CancellationToken cancellationToken)
{
var credentials = webProxy.Credentials as NetworkCredential;
var userId = credentials?.UserName ?? string.Empty;
var destinationAddress = await GetIPEndPointsAsync(context.DnsEndPoint, cancellationToken).FirstAsync();
var portBytes = BitConverter.GetBytes((ushort)context.DnsEndPoint.Port);
if (BitConverter.IsLittleEndian) Array.Reverse(portBytes);
var request = new byte[] { 0x04, 0x01 }
.Concat(portBytes)
.Concat(destinationAddress.Address.GetAddressBytes())
.Concat(Encoding.ASCII.GetBytes(userId))
.Concat(new byte[] { 0x00 })
.ToArray();
await stream.WriteAsync(request, cancellationToken);
var response = new byte[8];
await stream.ReadAsync(response, cancellationToken);
if (response[1] != 0x5A)
{
throw new Exception($"SOCKS4 proxy connection failed: {response[1]}");
}
}
private async Task ConnectSocks5Proxy(NetworkStream stream, SocketsHttpConnectionContext context, CancellationToken cancellationToken)
{
var credentials = webProxy.Credentials as NetworkCredential;
// SOCKS5 握手
byte[] authMethod = credentials != null ? new byte[] { 0x02 } : new byte[] { 0x00 };
await stream.WriteAsync(new byte[] { 0x05, 0x01 }.Concat(authMethod).ToArray(), cancellationToken);
var response = new byte[2];
await stream.ReadAsync(response, cancellationToken);
if (response[0] != 0x05)
{
throw new Exception("SOCKS5 handshake failed");
}
// 如果需要身份验证
if (response[1] == 0x02 && credentials != null)
{
var userBytes = Encoding.ASCII.GetBytes(credentials.UserName);
var passBytes = Encoding.ASCII.GetBytes(credentials.Password);
var authRequest = new byte[] { 0x01, (byte)userBytes.Length }
.Concat(userBytes)
.Concat(new byte[] { (byte)passBytes.Length })
.Concat(passBytes)
.ToArray();
await stream.WriteAsync(authRequest, cancellationToken);
var authResponse = new byte[2];
await stream.ReadAsync(authResponse, cancellationToken);
if (authResponse[1] != 0x00)
{
throw new Exception("SOCKS5 authentication failed");
}
}
// 发送连接请求
byte[] addressBytes;
byte addressType;
if (IPAddress.TryParse(context.DnsEndPoint.Host, out IPAddress? ipAddress))
{
addressBytes = ipAddress.GetAddressBytes();
addressType = (byte)(ipAddress.AddressFamily == AddressFamily.InterNetwork ? 1 : 4);
}
else
{
addressBytes = Encoding.ASCII.GetBytes(context.DnsEndPoint.Host);
addressType = 3; // 域名
}
var request = new byte[] { 0x05, 0x01, 0x00, addressType }
.Concat(addressType == 3 ? new[] { (byte)addressBytes.Length } : Array.Empty<byte>())
.Concat(addressBytes)
.Concat(BitConverter.GetBytes((ushort)context.DnsEndPoint.Port).Reverse())
.ToArray();
await stream.WriteAsync(request, cancellationToken);
var connectResponse = new byte[10];
await stream.ReadAsync(connectResponse, cancellationToken);
if (connectResponse[1] != 0x00)
{
throw new Exception($"SOCKS5 connection failed: {connectResponse[1]}");
}
}
/// <summary>
/// 连接回调
/// </summary>
@@ -256,11 +67,6 @@ sealed class ReverseProxyHttpClientHandler : DelegatingHandler
/// <returns></returns>
async ValueTask<Stream> ConnectCallback(SocketsHttpConnectionContext context, CancellationToken cancellationToken)
{
if (webProxy != null && !HttpNoProxy.IsNoProxy(webProxy))
{
return await ConnectThroughProxyAsync(context, cancellationToken);
}
var innerExceptions = new List<Exception>();
var ipEndPoints = GetIPEndPointsAsync(context.DnsEndPoint, cancellationToken);
@@ -1,4 +1,3 @@
using System.Net;
using MediaTypeHeaderValue = System.Net.Http.Headers.MediaTypeHeaderValue;
// ReSharper disable once CheckNamespace
@@ -141,6 +140,11 @@ sealed class HttpLocalRequestMiddleware
return null;
}
StringValues cookie = context.Request.Headers["cookie-steamTool"];
if (StringValues.IsNullOrEmpty(cookie))
cookie = context.Request.Headers["Cookie"];
StringValues referer = context.Request.Headers["Referer-steamTool"];
context.Response.Headers.AccessControlAllowOrigin = context.Request.Headers.Origin.Count == 0 ? "*" : context.Request.Headers.Origin;
context.Response.Headers.AccessControlAllowHeaders = "*";
context.Response.Headers.AccessControlAllowMethods = "*";
@@ -162,30 +166,29 @@ sealed class HttpLocalRequestMiddleware
Method = methodObj,
Content = hasReqContent ? new StreamContent(context.Request.Body) : null,
};
foreach (var item in context.Request.Headers)
req.Headers.UserAgent.ParseAdd(context.Request.Headers.UserAgent);
if (!StringValues.IsNullOrEmpty(cookie))
{
var headers = item.Key.ToLower();
if (headers.EndsWith("-steamtool"))
CookieHttpClient.CookieContainer.Add(new Cookie
{
CommentUri = requestUri,
Domain = requestUri.Host,
Value = cookie,
});
// req.Headers.Add("Cookie", cookie);
}
if (!StringValues.IsNullOrEmpty(referer))
{
try
{
var refererUri = new Uri(referer.ToString());
req.Headers.Referrer = refererUri;
}
catch
{
if (headers.Equals("cookie", StringComparison.OrdinalIgnoreCase))
{
CookieHttpClient.CookieContainer.Add(new Cookie
{
CommentUri = requestUri,
Domain = requestUri.Host,
Value = item.Value
});
}
if (headers.Equals("referer", StringComparison.OrdinalIgnoreCase))
{
var refererUri = new Uri(item.Value.ToString());
req.Headers.Referrer = refererUri;
}
req.Headers.TryAddWithoutValidation(item.Key.TrimEnd("-steamtool"), (IEnumerable<string?>)item.Value);
}
}
req.Headers.UserAgent.ParseAdd(context.Request.Headers.UserAgent);
if (hasReqContent)
{
req.Content!.Headers.ContentType = MediaTypeHeaderValue.Parse(context.Request.ContentType.ThrowIsNull());
@@ -1,6 +1,5 @@
// https://github.com/dotnetcore/FastGithub/blob/2.1.4/FastGithub.HttpServer/HttpReverseProxyMiddleware.cs
using Microsoft.AspNetCore.Http;
using Yarp.ReverseProxy.Forwarder;
// ReSharper disable once CheckNamespace
@@ -53,29 +52,7 @@ sealed partial class HttpReverseProxyMiddleware
if (TryGetDomainConfig(url, out var domainConfig) == false)
{
if (reverseProxyConfig.Service.TwoLevelAgentEnable)
{
var httpClient = httpClientFactory.CreateHttpClient("GlobalProxy", defaultDomainConfig);
var destinationPrefix = GetDestinationPrefix(context.Request.Scheme, context.Request.Host, null);
var forwarderRequestConfig = new ForwarderRequestConfig()
{
Version = context.Request.Protocol switch
{
var protocol when protocol.StartsWith("HTTP/2") => System.Net.HttpVersion.Version20,
var protocol when protocol.StartsWith("HTTP/3") => System.Net.HttpVersion.Version30,
_ => System.Net.HttpVersion.Version11,
},
};
var error = await httpForwarder.SendAsync(context, destinationPrefix, httpClient, forwarderRequestConfig, HttpTransformer.Empty);
if (error != ForwarderError.None)
{
await HandleErrorAsync(context, error);
}
}
else
{
await next(context);
}
await next(context);
return;
}
@@ -1,14 +1,11 @@
// https://github.com/dotnetcore/FastGithub/blob/2.1.4/FastGithub.Http/HttpClientFactory.cs
// ReSharper disable once CheckNamespace
using System.Net;
namespace BD.WTTS.Services.Implementation;
sealed class ReverseProxyHttpClientFactory : IReverseProxyHttpClientFactory
{
readonly IDomainResolver domainResolver;
readonly IWebProxy webProxy;
/// <summary>
/// 首次生命周期
@@ -30,19 +27,9 @@ sealed class ReverseProxyHttpClientFactory : IReverseProxyHttpClientFactory
/// </summary>
readonly ConcurrentDictionary<LifeTimeKey, Lazy<LifetimeHttpHandler>> httpHandlerLazyCache = new();
public ReverseProxyHttpClientFactory(IDomainResolver domainResolver, IReverseProxyConfig reverseProxyConfig)
public ReverseProxyHttpClientFactory(IDomainResolver domainResolver)
{
this.domainResolver = domainResolver;
if (reverseProxyConfig.Service.TwoLevelAgentEnable)
{
this.webProxy = new WebProxy($"{reverseProxyConfig.Service.TwoLevelAgentProxyType}://{reverseProxyConfig.Service.TwoLevelAgentIp}:{reverseProxyConfig.Service.TwoLevelAgentPortId}", true, null, null);
if (!string.IsNullOrEmpty(reverseProxyConfig.Service.TwoLevelAgentUserName) || !string.IsNullOrEmpty(reverseProxyConfig.Service.TwoLevelAgentPassword))
{
webProxy.Credentials = new NetworkCredential(reverseProxyConfig.Service.TwoLevelAgentUserName, reverseProxyConfig.Service.TwoLevelAgentPassword);
}
}
else
this.webProxy = HttpNoProxy.Instance;
}
public ReverseProxyHttpClient CreateHttpClient(string domain, IDomainConfig domainConfig)
@@ -56,7 +43,7 @@ sealed class ReverseProxyHttpClientFactory : IReverseProxyHttpClientFactory
}
LifetimeHttpHandler CreateLifetimeHttpHandler(LifeTimeKey lifeTimeKey, TimeSpan lifeTime)
=> new(domainResolver, webProxy, lifeTimeKey, lifeTime, OnLifetimeHttpHandlerDeactivate);
=> new(domainResolver, lifeTimeKey, lifeTime, OnLifetimeHttpHandlerDeactivate);
void OnLifetimeHttpHandlerDeactivate(LifetimeHttpHandler lifetimeHttpHandler)
{
@@ -164,13 +164,10 @@ abstract class ReverseProxyServiceImpl : IReverseProxySettings
{
if (string.IsNullOrWhiteSpace(reverseProxySettings.ProxyIp))
return StartProxyResultCode.Exception;
if (reverseProxySettings.ProxyMode == ProxyMode.Hosts)
var inUsePort = SocketHelper.IsUsePort(IPAddress.Parse(reverseProxySettings.ProxyIp!), 443);
if (inUsePort)
{
var inUsePort = SocketHelper.IsUsePort(IPAddress.Parse(reverseProxySettings.ProxyIp!), 443);
if (inUsePort)
{
return StartProxyResultCode.BindPortError;
}
return StartProxyResultCode.BindPortError;
}
}
@@ -80,19 +80,17 @@ sealed partial class YarpReverseProxyServiceImpl : ReverseProxyServiceImpl, IRev
builder.WebHost.UseKestrel(options =>
{
options.AddServerHeader = false;
options.RequestHeaderEncodingSelector = _ => Encoding.UTF8;
options.ResponseHeaderEncodingSelector = _ => Encoding.UTF8;
options.NoLimit();
#if WINDOWS
#if !NET7_0_OR_GREATER
if (OperatingSystem2.IsWindows7())
{
//https://github.com/dotnet/aspnetcore/issues/22563
options.ConfigureHttpsDefaults(httpsOptions =>
{
httpsOptions.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13;
});
}
if (OperatingSystem2.IsWindows7())
{
//https://github.com/dotnet/aspnetcore/issues/22563
options.ConfigureHttpsDefaults(httpsOptions =>
{
httpsOptions.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13;
});
}
#endif
//options.ListenSshReverseProxy();
//options.ListenGitReverseProxy();
@@ -63,7 +63,7 @@ partial class ProxyService
string? twoLevelAgentPassword = ProxySettings.TwoLevelAgentPassword.Value;
string? proxyDNS = ProxySettings.ProxyMasterDns.Value;
bool isSupportIpv6 = await RefreshIpv6Support();
bool useDoh = ProxySettings.UseDoh2.Value;
bool useDoh = ProxySettings.UseDoh.Value;
string? customDohAddres = ProxySettings.CustomDohAddres.Value;
Lazy<IPAddress> proxyIp_ = new(() => ReverseProxySettings.GetProxyIp(proxyIp));
@@ -1,7 +1,5 @@
// ReSharper disable once CheckNamespace
using Avalonia.Data;
using Google.Protobuf.WellKnownTypes;
using System.Linq;
namespace BD.WTTS.Services;
@@ -67,12 +65,12 @@ public sealed partial class ProxyService
.ToObservableChangeSet()
.AutoRefresh(x => x.ObservableItems)
.TransformMany(t => t.ObservableItems ?? new ObservableCollection<AccelerateProjectDTO>())
.AutoRefresh(x => x.ThreeStateEnable)
.WhenPropertyChanged(x => x.ThreeStateEnable, false)
.AutoRefresh(x => x.Checked)
.WhenPropertyChanged(x => x.Checked, false)
.Subscribe(_ =>
{
IsChangeSupportProxyServicesStatus = true;
ProxySettings.SupportProxyServicesStatus.Value = GetAccelerateEnableAllIds(EnableProxyDomains).ToImmutableHashSet();
ProxySettings.SupportProxyServicesStatus.Value = EnableProxyDomains?.Select(k => k.Id.ToString()).ToImmutableHashSet();
}));
this.WhenAnyValue(v => v.ProxyScripts)
@@ -154,10 +152,10 @@ public sealed partial class ProxyService
}
}
public IEnumerable<AccelerateProjectDTO> GetEnableProxyDomains()
public IEnumerable<AccelerateProjectDTO>? GetEnableProxyDomains()
{
if (!ProxyDomains.Items.Any_Nullable())
return [];
return null;
var data = ProxyDomains.Items
.Where(x => x.Items != null)
.SelectMany(s => s.Items!.Where(w => w.ThreeStateEnable != false));
@@ -165,7 +163,7 @@ public sealed partial class ProxyService
return data;
}
public IReadOnlyCollection<AccelerateProjectDTO>? EnableProxyDomains => GetEnableProxyDomains().ToImmutableArray();
public IReadOnlyCollection<AccelerateProjectDTO>? EnableProxyDomains => GetEnableProxyDomains()?.ToImmutableArray();
//static IEnumerable<AccelerateProjectDTO>? GetProxyDomainsItems(AccelerateProjectDTO accelerates)
//{
@@ -405,8 +403,22 @@ public sealed partial class ProxyService
{
var items = ProxyDomains.Items!.SelectMany(s => s.Items!);
var enableItems = ProxySettings.SupportProxyServicesStatus.Value;
RestoreAccelerateEnableAllIds(items, enableItems);
foreach (var item in items)
{
if (enableItems.Contains(item.Id.ToString()))
{
item.ThreeStateEnable = true;
}
}
}
//if (IsLoadImage && ProxyDomains.Items.Any_Nullable())
//{
// foreach (var item in ProxyDomains.Items)
// {
// item.ImageStream = ImageChannelType.AccelerateGroup.GetImageAsync(ImageUrlHelper.GetImageApiUrlById(item.ImageId));
// }
//}
}
public static bool IsChangeSupportProxyServicesStatus { get; set; }
@@ -450,29 +462,6 @@ public sealed partial class ProxyService
}
}
private IEnumerable<string> GetAccelerateEnableAllIds(IEnumerable<AccelerateProjectDTO>? nodes)
{
if (nodes == null)
return Enumerable.Empty<string>();
return nodes.Where(s => s.ThreeStateEnable != false).SelectMany(node => new[] { node.Id.ToString() }.Concat(GetAccelerateEnableAllIds(node.Items)));
}
private void RestoreAccelerateEnableAllIds(IEnumerable<AccelerateProjectDTO> nodes, IReadOnlyCollection<string> enableItems)
{
foreach (var node in nodes)
{
if (node.Items.Any_Nullable())
{
RestoreAccelerateEnableAllIds(node.Items, enableItems);
continue;
}
if (enableItems.Contains(node.Id.ToString()))
{
node.ThreeStateEnable = true;
}
}
}
Timer? timer;
public void StartTimer()
@@ -148,7 +148,7 @@ public partial interface IProxySettings
/// <summary>
/// 启用 DNS over HTTPS
/// </summary>
bool UseDoh2 { get; set; }
bool UseDoh { get; set; }
/// <summary>
/// 自定义 DNS over HTTPS 地址
@@ -268,7 +268,7 @@ public partial interface IProxySettings
/// <summary>
/// 启用 DNS over HTTPS的默认值
/// </summary>
const bool DefaultUseDoh2 = false;
const bool DefaultUseDoh = true;
/// <summary>
/// 自定义 DNS over HTTPS 地址的默认值
@@ -189,7 +189,7 @@ public sealed partial class ProxySettings_ : IProxySettings, ISettings, ISetting
/// 启用 DNS over HTTPS
/// </summary>
[MPKey(21), MP2Key(21), JsonPropertyOrder(21)]
public bool UseDoh2 { get; set; } = IProxySettings.DefaultUseDoh2;
public bool UseDoh { get; set; } = IProxySettings.DefaultUseDoh;
/// <summary>
/// 自定义 DNS over HTTPS 地址
@@ -356,8 +356,8 @@ public static partial class ProxySettings
/// <summary>
/// 启用 DNS over HTTPS
/// </summary>
public static SettingsStructProperty<bool, ProxySettings_> UseDoh2 { get; }
= new(DefaultUseDoh2);
public static SettingsStructProperty<bool, ProxySettings_> UseDoh { get; }
= new(DefaultUseDoh);
/// <summary>
/// 自定义 DNS over HTTPS 地址
@@ -11,8 +11,8 @@ public sealed class ProxySettingsWindowViewModel : WindowViewModel
Title = DisplayName;
}
//public IEnumerable<ExternalProxyType> ProxyTypes { get; }
// = Enum2.GetAll<ExternalProxyType>();
public IEnumerable<ExternalProxyType> ProxyTypes { get; }
= Enum2.GetAll<ExternalProxyType>();
public IEnumerable<string> ProxyDNSs { get; } = new[]
{
@@ -531,23 +531,6 @@
<StackPanel Margin="10">
<DockPanel>
<TextBlock Text="{Binding Name}" />
<Border
Padding="5,0"
HorizontalAlignment="Left"
CornerRadius="5"
IsVisible="{Binding ProxyType, Converter={StaticResource EnumEqualValueConverter}, ConverterParameter={x:Static enum:ProxyType.ServerAccelerate}}"
ToolTip.Tip="Beta 测试加速项,不稳定且随时可能调整">
<Border.Background>
<LinearGradientBrush StartPoint="0%,0%" EndPoint="100%,100%">
<GradientStop Offset="0.0" Color="#43cbff" />
<GradientStop Offset="1.0" Color="#9708cc" />
</LinearGradientBrush>
</Border.Background>
<TextBlock
VerticalAlignment="Center"
FontSize="10"
Text="Beta" />
</Border>
</DockPanel>
<Separator Classes="Separator" />
</StackPanel>
@@ -754,7 +737,7 @@
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
</StackPanel.Styles>
<Button Theme="{StaticResource TransparentButton}" ToolTip.ShowDelay="0">
<Button Theme="{StaticResource TransparentButton}">
<ToolTip.Tip>
<TextBlock>
<Run FontSize="16" Text="Q:为什么加速后流量一直显示 0KB ?" />
@@ -767,7 +750,7 @@
<Run Text="A:可以尝试执行以下操作进行修复,关闭加速,点击加速设置-移除证书再安装证书,重开加速。或者加速设置-代理设置-尝试更换doh地址或者关闭doh使用dns等方案。" />
</TextBlock>
</ToolTip.Tip>
<ui:FontIcon FontSize="20" Glyph="&#xE82F;" />
<ui:FontIcon FontSize="22" Glyph="&#xE82F;" />
</Button>
<TextBlock Text="{x:Static spp:Strings.CommunityFix_ProxyMode}" />
<TextBlock Text="{Binding Source={x:Static ms:ProxySettings.ProxyMode}, Path=Value, Mode=OneWay, Converter={StaticResource EnumLocalizationNameConverter}}" />
@@ -35,22 +35,17 @@
Text="{Binding Source={x:Static ms:ProxySettings.ProxyMasterDns}, Path=Value, Mode=TwoWay}" />
</ui:SettingsExpanderItem.Footer>
</ui:SettingsExpanderItem>
<ui:SettingsExpanderItem Description="{Binding Path=Res.Settings_Proxy_SystemProxyPortId_Tip, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<TextBlock Text="{Binding Path=Res.Settings_Proxy_SystemProxyPortId, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:SettingsExpanderItem.Footer>
<NumericUpDown
MaxWidth="200"
FormatString="0"
Maximum="65535"
Value="{Binding Source={x:Static ms:ProxySettings.SystemProxyPortId}, Path=Value, Mode=TwoWay}" />
</ui:SettingsExpanderItem.Footer>
</ui:SettingsExpanderItem>
<!--<DockPanel>
<Label Content="监听端口"></Label>
<NumericUpDown Value="{Binding Source={x:Static ms:ProxySettings.HostProxyPortId},Path=Value, Mode=TwoWay}"
FormatString="0"
Maximum="65535"/>
</DockPanel>-->
<!--<DockPanel>
<TextBlock Text="IPV6支持:" />
<TextBlock Text="IPV6地址" ></TextBlock>
<Button Content="{Binding Path=Res.Refresh,Mode=OneWay,Source={x:Static s:ResourceService.Current}}"></Button>
<Button Content="{Binding Path=Res.Refresh,Mode=OneWay,Source={x:Static resx:R.Current}}"></Button>
</DockPanel>-->
<ui:SettingsExpanderItem>
<TextBlock Text="{Binding Path=Res.CommunityFix_AutoRunProxy, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
@@ -69,7 +64,7 @@
<ui:SettingsExpanderItem>
<TextBlock Text="{Binding Path=Res.CommunityFix_EnableUseDoh, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:SettingsExpanderItem.Footer>
<ToggleSwitch Classes="LeftContent" IsChecked="{Binding Source={x:Static ms:ProxySettings.UseDoh2}, Path=Value, Mode=TwoWay}" />
<ToggleSwitch Classes="LeftContent" IsChecked="{Binding Source={x:Static ms:ProxySettings.UseDoh}, Path=Value, Mode=TwoWay}" />
</ui:SettingsExpanderItem.Footer>
</ui:SettingsExpanderItem>
@@ -94,72 +89,12 @@
<Button Content="选择证书"/>
</DockPanel>-->
<ui:SettingsExpanderItem Description="*该选项勾选后会导致程序的加速完全不生效,请确认是配合二级代理实现仅需要脚本功能后再启用">
<ui:SettingsExpanderItem Description="*该选项勾选后会导致程序的加速完全不生效,请确认是仅需要脚本功能后再启用">
<TextBlock Text="{Binding Path=Res.CommunityFix_ProxyOnlyOnScript, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:SettingsExpanderItem.Footer>
<ToggleSwitch Classes="LeftContent" IsChecked="{Binding Source={x:Static ms:ProxySettings.OnlyEnableProxyScript}, Path=Value, Mode=TwoWay}" />
</ui:SettingsExpanderItem.Footer>
</ui:SettingsExpanderItem>
</ui:SettingsExpander>
<ui:SettingsExpander
Description="*改动后需重启加速才能生效"
Header="{Binding Path=Res.Settings_Proxy_TwoLevelAgent, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
IsExpanded="True">
<ui:SettingsExpander.Footer>
<ToggleSwitch IsChecked="{Binding Source={x:Static ms:ProxySettings.TwoLevelAgentEnable}, Path=Value, Mode=TwoWay}" />
</ui:SettingsExpander.Footer>
<ui:SettingsExpanderItem>
<TextBlock Text="{Binding Path=Res.Settings_Proxy_ProxyType, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:SettingsExpanderItem.Footer>
<ui:FAComboBox MinWidth="200" SelectedItem="{Binding Source={x:Static ms:ProxySettings.TwoLevelAgentProxyType}, Path=Value, Mode=TwoWay}">
<ui:FAComboBox.Items>
<spp:ExternalProxyType>Http</spp:ExternalProxyType>
<spp:ExternalProxyType>Socks4</spp:ExternalProxyType>
<spp:ExternalProxyType>Socks5</spp:ExternalProxyType>
</ui:FAComboBox.Items>
</ui:FAComboBox>
</ui:SettingsExpanderItem.Footer>
</ui:SettingsExpanderItem>
<ui:SettingsExpanderItem>
<TextBlock Text="{Binding Path=Res.Settings_Proxy_IPAddress, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:SettingsExpanderItem.Footer>
<TextBox MinWidth="200" Text="{Binding Source={x:Static ms:ProxySettings.TwoLevelAgentIp}, Path=Value, Mode=TwoWay}" />
</ui:SettingsExpanderItem.Footer>
</ui:SettingsExpanderItem>
<ui:SettingsExpanderItem>
<TextBlock Text="{Binding Path=Res.Settings_Proxy_Port, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:SettingsExpanderItem.Footer>
<NumericUpDown
MinWidth="200"
FormatString="0"
Maximum="65535"
Minimum="80"
Value="{Binding Source={x:Static ms:ProxySettings.TwoLevelAgentPortId}, Path=Value, Mode=TwoWay}" />
</ui:SettingsExpanderItem.Footer>
</ui:SettingsExpanderItem>
<ui:SettingsExpanderItem>
<TextBlock Text="{Binding Path=Res.Settings_Proxy_UserName, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:SettingsExpanderItem.Footer>
<TextBox MinWidth="200" Text="{Binding Source={x:Static ms:ProxySettings.TwoLevelAgentUserName}, Path=Value, Mode=TwoWay}" />
</ui:SettingsExpanderItem.Footer>
</ui:SettingsExpanderItem>
<ui:SettingsExpanderItem>
<TextBlock Text="{Binding Path=Res.Settings_Proxy_Password, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:SettingsExpanderItem.Footer>
<TextBox
MinWidth="200"
Classes="revealPasswordButton"
PasswordChar="{StaticResource PasswordChar}"
Text="{Binding Source={x:Static ms:ProxySettings.TwoLevelAgentPassword}, Path=Value, Mode=TwoWay}" />
</ui:SettingsExpanderItem.Footer>
</ui:SettingsExpanderItem>
</ui:SettingsExpander>
</StackPanel>
</UserControl>
@@ -475,27 +475,6 @@ public static class AuthenticatorHelper
return filePath;
}
public static async Task<IEnumerable<IFileResult>?> MultipleSelectFolderPath(string? fileExtension = null)
{
var options = new PickOptions();
if (!string.IsNullOrEmpty(fileExtension))
{
AvaloniaFilePickerFileTypeFilter fileTypes = new AvaloniaFilePickerFileTypeFilter.Item[] {
new($"{fileExtension} Files") {
Patterns = new[] { $"*{fileExtension}", },
//MimeTypes
//AppleUniformTypeIdentifiers =
},
};
options.FileTypes = fileTypes;
}
var filePaths = await FilePicker2.PickMultipleAsync(options);
if (!filePaths.Any_Nullable())
Toast.Show(ToastIcon.Warning, Strings.FilePathNotExist);
return filePaths;
}
public static async Task<bool> VerifyMaxValue()
{
var auths = await AuthenticatorHelper.GetAllSourceAuthenticatorAsync();
@@ -1,6 +1,5 @@
using BD.WTTS.UI.Views.Pages;
using WinAuth;
using static SteamKit2.DepotManifest;
using SJsonSerializer = System.Text.Json.JsonSerializer;
namespace BD.WTTS.UI.ViewModels;
@@ -17,8 +16,6 @@ public class AuthenticatorImportPageViewModel : ViewModelBase
{
AuthenticatorImportMethods = new List<AuthenticatorImportMethod>
{
new AuthenticatorImportMethod(Strings.LocalAuth_Import.Format(Strings.LocalAuth_JoinSteamAuthenticator), Strings.LocalAuth_JoinSteamAuthenticatorDesc,
PageType: typeof(JoinSteamAuthenticatorPage)),
new AuthenticatorImportMethod(Strings.Auth_SteamLoginImport, Strings.Steam_UserLoginTip,
PageType: typeof(SteamLoginImportPage)),
new AuthenticatorImportMethod(Strings.LocalAuth_Import.Format(Strings.SteamGuard), Strings.LocalAuth_SteamGuardImport,
@@ -29,6 +26,8 @@ public class AuthenticatorImportPageViewModel : ViewModelBase
if (await AuthenticatorHelper.VerifyMaxValue())
await ImportFromSdaFile();
})),
new AuthenticatorImportMethod(Strings.LocalAuth_Import.Format(Strings.LocalAuth_JoinSteamAuthenticator), Strings.LocalAuth_JoinSteamAuthenticatorDesc,
PageType: typeof(JoinSteamAuthenticatorPage)),
new AuthenticatorImportMethod(Strings.LocalAuth_Import.Format(Strings.WattToolKitV2), Strings.LocalAuth_WattToolKitV2Import,
Command: ReactiveCommand.Create(async () =>
{
@@ -69,40 +68,34 @@ public class AuthenticatorImportPageViewModel : ViewModelBase
{
try
{
var filePaths = await AuthenticatorHelper.MultipleSelectFolderPath(FileEx.maFile);
var filePath = await AuthenticatorHelper.SelectFolderPath(FileEx.maFile);
if (!filePaths.Any_Nullable())
return;
if (string.IsNullOrEmpty(filePath)) return;
foreach (var file in filePaths)
var text = await File.ReadAllTextAsync(filePath);
var sdaFileModel = SJsonSerializer.Deserialize(text, ImportFileModelJsonContext.Default.SdaFileModel);
sdaFileModel.ThrowIsNull();
var steamDataModel = new SdaFileConvertToSteamDataModel(sdaFileModel);
SteamAuthenticator steamAuthenticator = new()
{
if (!File.Exists(file.FullPath)) return;
var text = await File.ReadAllTextAsync(file.FullPath);
DeviceId = sdaFileModel.DeviceId,
Serial = sdaFileModel.SerialNumber,
SecretKey = Convert.FromBase64String(sdaFileModel.SharedSecret),
SteamData = SJsonSerializer.Serialize(steamDataModel, ImportFileModelJsonContext.Default.SdaFileConvertToSteamDataModel),
};
var sdaFileModel = SJsonSerializer.Deserialize(text, ImportFileModelJsonContext.Default.SdaFileModel);
sdaFileModel.ThrowIsNull();
var steamDataModel = new SdaFileConvertToSteamDataModel(sdaFileModel);
SteamAuthenticator steamAuthenticator = new()
var authDto =
new AuthenticatorDTO()
{
DeviceId = sdaFileModel.DeviceId,
Serial = sdaFileModel.SerialNumber,
SecretKey = Convert.FromBase64String(sdaFileModel.SharedSecret),
SteamData = SJsonSerializer.Serialize(steamDataModel, ImportFileModelJsonContext.Default.SdaFileConvertToSteamDataModel),
Name = $"(Steam){steamAuthenticator.AccountName}",
Value = steamAuthenticator,
Created = DateTimeOffset.Now,
};
var authDto =
new AuthenticatorDTO()
{
Name = $"(Steam){steamAuthenticator.AccountName}",
Value = steamAuthenticator,
Created = DateTimeOffset.Now,
};
if (await AuthenticatorHelper.SaveAuthenticator(authDto))
{
Toast.Show(ToastIcon.Success, Strings.ModelContent_ImportSuccessful_.Format(authDto.Name));
}
if (await AuthenticatorHelper.SaveAuthenticator(authDto))
{
Toast.Show(ToastIcon.Success, Strings.ModelContent_ImportSuccessful_.Format(authDto.Name));
}
}
catch (Exception e)
@@ -1,7 +1,8 @@
using AppResources = BD.WTTS.Client.Resources.Strings;
using BD.SteamClient.Models;
using BD.SteamClient.Services;
using WinAuth;
using AppResources = BD.WTTS.Client.Resources.Strings;
using WSteamClient = WinAuth.SteamClient;
namespace BD.WTTS.UI.ViewModels;
@@ -72,55 +73,11 @@ public sealed partial class SteamTradePageViewModel : WindowViewModel
}
else
{
IsLogged = await CheckAndRefreshToken();
IsLogged = true;
await GetConfirmations(_steamClient);
}
}
public async ValueTask<bool> CheckAndRefreshToken()
{
IsLoading = true;
// 不具备检查条件 没有保持的会话信息
if (_steamClient == null)
return false;
if (_steamClient.Session == null || _steamAuthenticator == null)
return false;
// token 依旧有效 直接返回
if (!_steamClient.Session.IsAccessTokenExpired())
return true;
try
{
// 是否有 refresh token
string? refreshToken = _steamClient.Session?.RefreshToken;
if (string.IsNullOrWhiteSpace(refreshToken))
return false;
var session = _steamClient.Session;
// 刷新 access token (有效时间同样为一天)
await session!.RefreshAccessToken(_steamClient);
// 使用原先获取的具体派生类型 修改数据
_steamAuthenticator.SessionData = session.ToString();
// 通过原始令牌引用保存信息
await AuthenticatorHelper.SaveAuthenticator(_authenticatorDto)
.ConfigureAwait(false);
return true;
}
finally
{
IsLoading = false;
}
}
public async Task Refresh()
{
if (_steamClient?.IsLoggedIn() == true)
@@ -357,6 +314,7 @@ public sealed partial class SteamTradePageViewModel : WindowViewModel
// Content = Strings.LocalAuth_AuthTrade_MessageBoxTip_.Format(statusText)
// }, statusText, isCancelButton: true, isDialog: false))
//{
Toast.Show(ToastIcon.Info, Strings.LocalAuth_AuthTrade_ConfirmTip_.Format(statusText));
if (!await ChangeTradeStatus(status, selectedList)) return;
@@ -123,59 +123,28 @@
Text="{Binding Path=Res.GameRelated_Borderless_MoveMouseTip, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
</WrapPanel>
<Grid
<StackPanel
HorizontalAlignment="Center"
ColumnDefinitions="Auto,*"
IsVisible="{ReflectionBinding !!SelectWindow}"
RowDefinitions="Auto,Auto,Auto,Auto">
<TextBlock VerticalAlignment="Center" Text="{Binding Path=Res.GameRelated_Borderless_Title, Mode=OneWay, Source={x:Static s:ResourceService.Current}, StringFormat={}{0}:}" />
<TextBox
Grid.Column="1"
Margin="0,0,0,10"
IsReadOnly="True"
Text="{ReflectionBinding SelectWindow.Title,
Mode=OneWay}"
TextWrapping="WrapWithOverflow" />
<TextBlock
Grid.Row="1"
VerticalAlignment="Center"
Text="{Binding Path=Res.GameRelated_Borderless_Process, Mode=OneWay, Source={x:Static s:ResourceService.Current}, StringFormat={}{0}:}" />
<TextBox
Grid.Row="1"
Grid.Column="1"
Margin="0,0,0,10"
IsReadOnly="True"
Text="{ReflectionBinding SelectWindow.Title,
Mode=OneWay}"
TextWrapping="WrapWithOverflow" />
<TextBlock
Grid.Row="2"
VerticalAlignment="Center"
Text="{Binding Path=Res.GameRelated_Borderless_ClassName, Mode=OneWay, Source={x:Static s:ResourceService.Current}, StringFormat={}{0}:}" />
<TextBox
Grid.Row="2"
Grid.Column="1"
Margin="0,0,0,10"
IsReadOnly="True"
Text="{ReflectionBinding SelectWindow.Name,
Mode=OneWay}"
TextWrapping="WrapWithOverflow" />
<TextBlock
Grid.Row="3"
VerticalAlignment="Center"
Text="{Binding Path=Res.GameRelated_Borderless_Path, Mode=OneWay, Source={x:Static s:ResourceService.Current}, StringFormat={}{0}:}" />
<TextBox
Grid.Row="3"
Grid.Column="1"
Margin="0,0,0,10"
IsReadOnly="True"
Text="{ReflectionBinding SelectWindow.Path,
Mode=OneWay}"
TextWrapping="WrapWithOverflow" />
</Grid>
Orientation="Vertical"
Spacing="10">
<TextBlock TextWrapping="WrapWithOverflow">
<Run Text="{Binding Path=Res.GameRelated_Borderless_Title, Mode=OneWay, Source={x:Static s:ResourceService.Current}, StringFormat={}{0}:}" />
<Run Text="{ReflectionBinding SelectWindow.Title, Mode=OneWay}" />
</TextBlock>
<TextBlock TextWrapping="WrapWithOverflow">
<Run Text="{Binding Path=Res.GameRelated_Borderless_ClassName, Mode=OneWay, Source={x:Static s:ResourceService.Current}, StringFormat={}{0}:}" />
<Run Text="{ReflectionBinding SelectWindow.ClassName, Mode=OneWay}" />
</TextBlock>
<TextBlock TextWrapping="WrapWithOverflow">
<Run Text="{Binding Path=Res.GameRelated_Borderless_Process, Mode=OneWay, Source={x:Static s:ResourceService.Current}, StringFormat={}{0}:}" />
<Run Text="{ReflectionBinding SelectWindow.Name, Mode=OneWay}" />
</TextBlock>
<TextBlock TextWrapping="WrapWithOverflow">
<Run Text="{Binding Path=Res.GameRelated_Borderless_Path, Mode=OneWay, Source={x:Static s:ResourceService.Current}, StringFormat={}{0}:}" />
<Run Text="{ReflectionBinding SelectWindow.Path, Mode=OneWay}" />
</TextBlock>
</StackPanel>
<WrapPanel HorizontalAlignment="Center" Orientation="Horizontal">
<WrapPanel.Styles>
@@ -2,7 +2,6 @@ using BD.SteamClient.Models;
using BD.SteamClient.Models.Idle;
using BD.SteamClient.Services;
using BD.SteamClient.Constants;
using Avalonia.Threading;
namespace BD.WTTS.UI.ViewModels;
@@ -130,7 +129,13 @@ public sealed partial class IdleCardPageViewModel : ViewModelBase
}
else
{
StopIdleRun();
RunState = false;
StopIdle();
RunOrStopAutoNext(false);
RunOrStopAutoCardDropCheck(false);
IdleTime = default;
ResetCurrentIdle();
ChangeRunTxt();
}
RunLoaingState = false;
@@ -142,17 +147,6 @@ public sealed partial class IdleCardPageViewModel : ViewModelBase
}
}
private void StopIdleRun()
{
RunState = false;
StopIdle();
RunOrStopAutoNext(false);
RunOrStopAutoCardDropCheck(false);
IdleTime = default;
ResetCurrentIdle();
ChangeRunTxt();
}
/// <summary>
/// 手动切换下一个游戏
/// </summary>
@@ -313,26 +307,17 @@ public sealed partial class IdleCardPageViewModel : ViewModelBase
{
try
{
var isTokenAccess = Ioc.Get<ISteamAccountService>().IsAccessTokenValid(SteamLoginState.AccessToken!);
var isTokenAccess = await Ioc.Get<ISteamAccountService>().CheckAccessTokenValidation(SteamLoginState.AccessToken!);
if (isTokenAccess)
{
await RunLoadBadges();
}
else
{
var new_accessToken = await Ioc.Get<ISteamAccountService>().RefreshAccessToken(SteamLoginState.SteamId, SteamLoginState.RefreshToken!);
if (new_accessToken is not null) // 刷新Token
{
await RefreshAccessTokenAsync(new_accessToken);
await RunLoadBadges();
}
else // 重新登录
{
await ISecureStorage.Instance.RemoveAsync(ISteamSessionService.CurrentSteamUserKey);
SteamLoginState = new();
await LoginSteam();
await RunLoadBadges();
}
await ISecureStorage.Instance.RemoveAsync(ISteamSessionService.CurrentSteamUserKey);
SteamLoginState = new();
await LoginSteam();
await RunLoadBadges();
}
return true;
}
@@ -392,19 +377,17 @@ public sealed partial class IdleCardPageViewModel : ViewModelBase
{
try
{
IdleGameList.Clear();
var badges = Badges.Where(w => w.CardsRemaining != 0);
var apps = (SteamIdleSettings.IdleSequentital.Value switch
var apps = SteamIdleSettings.IdleSequentital.Value switch
{
IdleSequentital.LeastCards => badges.OrderBy(o => o.CardsRemaining).Select(s => new IdleApp(s)),
IdleSequentital.Mostcards => badges.OrderByDescending(o => o.CardsRemaining).Select(s => new IdleApp(s)),
IdleSequentital.Mostvalue => badges.OrderByDescending(o => o.RegularAvgPrice).Select(s => new IdleApp(s)),
_ => badges.Select(s => new IdleApp(s)),
}).ToImmutableList();
Dispatcher.UIThread.Invoke(() =>
{
IdleGameList.Clear();
EnumerableExtensions.AddRange(IdleGameList, apps);
});
};
foreach (var app in apps)
IdleGameList.Add(app);
return true;
}
catch (Exception ex)
@@ -794,23 +777,6 @@ public sealed partial class IdleCardPageViewModel : ViewModelBase
Toast.Show(ToastIcon.Success, message);
INotificationService.Instance.Notify(message, NotificationType.Message);
}
private async Task RefreshAccessTokenAsync(string new_accessToken)
{
SteamLoginState.AccessToken = new_accessToken;
SteamSession session = new SteamSession()
{
SteamId = SteamLoginState.SteamId.ToString(),
AccessToken = SteamLoginState.AccessToken,
RefreshToken = SteamLoginState.RefreshToken
};
session.GenerateSetCookie();
var sessionService = Ioc.Get<ISteamSessionService>();
sessionService.AddOrSetSeesion(session);
if (LoginViewModel is null || LoginViewModel.RemenberLogin)
await sessionService.SaveSession(session);
}
#endregion
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -0,0 +1,17 @@
namespace BD;
[AttributeUsage(AttributeTargets.All, Inherited = false)]
public sealed class MobiusAttribute : Attribute
{
public MobiusAttribute()
{
}
public MobiusAttribute(string str)
{
}
public bool Obsolete { get; set; }
}
@@ -0,0 +1,17 @@
namespace BD;
[AttributeUsage(AttributeTargets.All, Inherited = false)]
public sealed class MobiusAttribute : Attribute
{
public MobiusAttribute()
{
}
public MobiusAttribute(string str)
{
}
public bool Obsolete { get; set; }
}
@@ -10,6 +10,8 @@ partial interface IApplication
/// </summary>
/// <param name="iconKey"></param>
/// <returns></returns>
[Mobius(Obsolete = true)]
[Obsolete]
string? GetIconSourceByIconKey(string? iconKey) => string.IsNullOrWhiteSpace(iconKey) ? null :
#if WINDOWS || MACCATALYST || MACOS || LINUX
$"avares://BD.WTTS.Client.Avalonia.App/UI/Assets/Icons/{iconKey}.ico";
@@ -22,6 +24,8 @@ partial interface IApplication
/// </summary>
/// <param name="typeName"></param>
/// <returns></returns>
[Mobius(Obsolete = true)]
[Obsolete]
string GetIconKeyByTypeName(string typeName)
{
#if WINDOWS || MACCATALYST || MACOS || LINUX
@@ -12,6 +12,12 @@ partial interface IApplication
/// <param name="msgToast"></param>
/// <param name="showToast"></param>
/// <returns></returns>
[Mobius(
"""
App // ICommand
async void CopyToClipboardCommandCore(object? text)
ICommand CopyToClipboardCommand
""")]
static async Task CopyToClipboardAsync(string? text, string? msgToast = null, bool showToast = true)
{
if (!string.IsNullOrWhiteSpace(text))
@@ -3,37 +3,65 @@ namespace BD.WTTS;
partial interface IApplication : IDisposableHolder
{
[Mobius(
"""
App.CompositeDisposable
""")]
new CompositeDisposable CompositeDisposable { get; }
/// <summary>
/// 切换当前桌面应用的主题而不改变设置值
/// </summary>
/// <param name="value"></param>
[Mobius(
"""
App.SetThemeNotChangeValue
""")]
void SetThemeNotChangeValue(AppTheme value) { }
/// <summary>
/// 退出整个程序
/// </summary>
[Mobius(
"""
App.Shutdown
""")]
void Shutdown() { }
/// <summary>
/// 主窗口恢复显示
/// </summary>
[Mobius(
"""
App.RestoreMainWindow
""")]
void RestoreMainWindow() { }
/// <summary>
/// 主窗口置顶一次
/// </summary>
[Mobius(
"""
App.RestoreMainWindow
""")]
void SetTopmostOneTime() { }
/// <summary>
/// 托盘菜单
/// </summary>
[Mobius(
"""
App.TrayIconMenus
""")]
IReadOnlyDictionary<string, ICommand> TrayIconMenus { get; }
/// <summary>
/// 是否有活动窗口
/// </summary>
/// <returns></returns>
[Mobius(
"""
App.HasActiveWindow
""")]
bool HasActiveWindow();
}
@@ -5,8 +5,16 @@ partial interface IApplication
{
#region Logger
[Mobius(
"""
Mobius.Helpers.LogInitHelper
""")]
const LogLevel DefaultLoggerMinLevel = IPCSubProcessFileSystem.DefaultLoggerMinLevel;
[Mobius(
"""
Mobius.Helpers.LogInitHelper
""")]
public static NLogLevel DefaultNLoggerMinLevel => IPCSubProcessFileSystem.DefaultNLoggerMinLevel;
/// <summary>
@@ -14,12 +22,24 @@ partial interface IApplication
/// </summary>
/// <param name="logLevel">level to be converted.</param>
/// <returns></returns>
[Mobius(
"""
Mobius.Helpers.LogInitHelper
""")]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static NLogLevel ConvertLogLevel(LogLevel logLevel) => IPCSubProcessFileSystem.ConvertLogLevel(logLevel);
[Mobius(
"""
Mobius.Helpers.LogInitHelper
""")]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static void SetNLoggerMinLevel(LogLevel logLevel) => SetNLoggerMinLevel(ConvertLogLevel(logLevel));
[Mobius(
"""
Mobius.Helpers.LogInitHelper
""")]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static void SetNLoggerMinLevel(NLogLevel logLevel)
{
@@ -27,6 +47,7 @@ partial interface IApplication
NInternalLogger.LogLevel = logLevel;
}
[Mobius(Obsolete = true)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void TrySetLoggerMinLevel(LogLevel logLevel)
{
@@ -41,11 +62,19 @@ partial interface IApplication
SetNLoggerMinLevel(ConvertLogLevel(logLevel));
}
[Mobius(
"""
Mobius.Helpers.LogInitHelper
""")]
private static LoggerFilterOptions? _LoggerFilterOptions;
/// <summary>
/// 日志过滤选项
/// </summary>
[Mobius(
"""
Mobius.Helpers.LogInitHelper
""")]
static LoggerFilterOptions? LoggerFilterOptions
{
get
@@ -59,6 +88,10 @@ partial interface IApplication
/// <summary>
/// 配置 NLog 提供程序,仅初始化时调用
/// </summary>
[Mobius(
"""
Mobius.Helpers.LogInitHelper.GetMobiusLoggingBuilder
""")]
public static Action<ILoggingBuilder> ConfigureLogging(LogLevel minLevel = DefaultLoggerMinLevel)
{
return (ILoggingBuilder builder) =>
@@ -92,6 +125,10 @@ partial interface IApplication
/// <summary>
/// 日志最低等级
/// </summary>
[Mobius(
"""
Mobius.Helpers.LogInitHelper
""")]
public static LogLevel LoggerMinLevel
{
get
@@ -119,6 +156,7 @@ partial interface IApplication
/// <summary>
/// 启用日志
/// </summary>
[Mobius(Obsolete = true)]
public static bool EnableLogger
{
get => LoggerMinLevel > LogLevel.None;
@@ -135,6 +173,10 @@ partial interface IApplication
/// <summary>
/// 日志存放文件夹路径
/// </summary>
[Mobius(
"""
MobiusHost.LogDirPath
""")]
static string LogDirPath { get; internal set; } = string.Empty;
#if (WINDOWS || MACCATALYST || MACOS || LINUX) && !(IOS || ANDROID)
@@ -142,6 +184,7 @@ partial interface IApplication
/// <summary>
/// 日志存放文件夹路径(ASF)
/// </summary>
[Mobius(Obsolete = true)]
static string LogDirPathASF { get; set; } = string.Empty;
#endif
@@ -154,6 +197,7 @@ partial interface IApplication
static bool LogUnderCache => true;
#endif
[Mobius(Obsolete = true)]
static Action? ASFInitCoreLoggers { get; private set; }
#endregion
@@ -3,6 +3,10 @@ namespace BD.WTTS;
partial interface IApplication
{
[Mobius(
"""
MobiusHost.EnableDevTools
""")]
public static bool EnableDevtools { get; set; } =
#if DEBUG
true
@@ -14,10 +18,18 @@ partial interface IApplication
/// <summary>
/// 禁用 GPU 硬件加速
/// </summary>
[Mobius(
"""
MobiusHost.DisableGPU
""")]
public static bool DisableGPU { get; set; }
/// <summary>
/// 使用 native OpenGL(仅 Windows)
/// </summary>
[Mobius(
"""
MobiusHost.UseWgl
""")]
public static bool UseWgl { get; set; }
}
@@ -20,11 +20,19 @@ partial interface IApplication
}
#endif
[Mobius(
"""
AppConstants.ProgramName
""")]
private static string? _ProgramName;
/// <summary>
/// 获取当前主程序文件名,例如 steam++.exe
/// </summary>
[Mobius(
"""
AppConstants.ProgramName
""")]
static string ProgramName
{
get
@@ -34,6 +42,10 @@ partial interface IApplication
}
}
[Mobius(
"""
AppConstants.ProgramName
""")]
private static string GetProgramName()
{
var processPath = Environment.ProcessPath;
@@ -6,11 +6,19 @@ partial interface IApplication
/// <summary>
/// 获取或设置当前应用的主题,例如跟随系统,浅色,深色
/// </summary>
[Mobius(
"""
App.Theme
""")]
AppTheme Theme { get; set; }
/// <summary>
/// 获取当前应用的实际主题
/// </summary>
[Mobius(
"""
App.ActualTheme
""")]
AppTheme ActualTheme => Theme switch
{
AppTheme.FollowingSystem => GetActualThemeByFollowingSystem(),
@@ -22,8 +30,13 @@ partial interface IApplication
/// <summary>
/// 获取当前应用的默认主题
/// </summary>
[Mobius(
"""
App.DefaultActualTheme
""")]
protected AppTheme DefaultActualTheme { get; }
[Mobius(Obsolete = true)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static AppTheme GetAppThemeByIsLightOrDarkTheme(bool isLightOrDarkTheme) => isLightOrDarkTheme ? AppTheme.Light : AppTheme.Dark;
@@ -31,6 +44,10 @@ partial interface IApplication
/// 获取当前应用主题跟随系统时的实际主题
/// </summary>
/// <returns></returns>
[Mobius(
"""
App.GetActualThemeByFollowingSystem
""")]
protected AppTheme GetActualThemeByFollowingSystem()
{
var dps = IPlatformService.Instance;
@@ -2,6 +2,10 @@ namespace BD.WTTS;
public partial interface IApplication
{
[Mobius(
"""
App.TrayMenus
""")]
Dictionary<string, TrayMenuItem>? TrayMenus { get; }
/// <summary>
@@ -9,5 +13,9 @@ public partial interface IApplication
/// </summary>
/// <param name="menuKey"></param>
/// <param name="trayMenuItem"></param>
[Mobius(
"""
App.UpdateMenuItems
""")]
void UpdateMenuItems(string menuKey, TrayMenuItem trayMenuItem);
}
+34
View File
@@ -6,6 +6,12 @@ namespace BD.WTTS;
/// </summary>
public partial interface IApplication
{
[Mobius(
"""
App // ICommand
async void CopyToClipboardCommandCore(object? text)
ICommand CopyToClipboardCommand
""")]
static IApplication Instance => Ioc.Get<IApplication>();
/// <summary>
@@ -13,6 +19,7 @@ public partial interface IApplication
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[Mobius(Obsolete = true)]
static bool IsDesktop()
#if WINDOWS || MACOS || LINUX
=> true;
@@ -24,6 +31,7 @@ public partial interface IApplication
=> OperatingSystem.IsWindows() || OperatingSystem.IsMacOS() || (OperatingSystem.IsLinux() && !OperatingSystem.IsAndroid());
#endif
[Mobius(Obsolete = true)]
[Obsolete("use IsDesktop", true)]
static bool IsDesktopPlatform => IsDesktop();
@@ -31,6 +39,7 @@ public partial interface IApplication
/// 获取当前平台 UI Host
/// <para>reference to the ViewController (if using Xamarin.iOS), Activity (if using Xamarin.Android) IWin32Window or IntPtr (if using .Net Framework).</para>
/// </summary>
[Mobius(Obsolete = true)]
object CurrentPlatformUIHost { get; }
#if WINDOWS
@@ -52,6 +61,7 @@ public partial interface IApplication
});
#endif
[Mobius(Obsolete = true)]
DeploymentMode DeploymentMode =>
#if WINDOWS
_DeploymentMode.Value;
@@ -60,8 +70,16 @@ public partial interface IApplication
#endif
/// <inheritdoc cref="IPlatformService.SetBootAutoStart(bool, string)"/>
[Mobius(
"""
SystemBootHelper.SetBootAutoStart
""")]
static void SetBootAutoStart(bool isAutoStart) => IPlatformService.Instance.SetBootAutoStart(isAutoStart, Constants.HARDCODED_APP_NAME);
[Mobius(
"""
ISppWebApiService.clientPlatform
""")]
private static readonly Lazy<ClientPlatform> clientPlatform = new(() =>
{
var platform = DeviceInfo2.Platform();
@@ -145,13 +163,29 @@ public partial interface IApplication
return default;
});
[Mobius(
"""
App.GetApplicationIcon
""")]
static byte[] Login_512 => Properties.Resources.AppLogo_512;
[Mobius(
"""
ISppWebApiService.clientPlatform
""")]
static ClientPlatform ClientPlatform => clientPlatform.Value;
[Mobius(Obsolete = true)]
System.Drawing.Size? GetScreenSize() => null;
[Mobius(Obsolete = true)]
bool? IsAnyWindowNotMinimized() => null;
[Mobius(
"""
App // ICommand
async void OpenBrowserCommandCore(object? url)
ICommand OpenBrowserCommand
""")]
async void OpenBrowserCommandCore(object? url) => await Browser2.OpenAsync(url?.ToString());
}
@@ -5,6 +5,8 @@ namespace BD.WTTS;
/// 指定应用此特性的类需要指定的授权
/// </summary>
[AttributeUsage(AttributeTargets.Class, Inherited = true)]
[Mobius(Obsolete = true)]
[Obsolete]
public sealed class AuthorizeAttribute : Attribute
{
public static bool HasAuthorize(object obj)
@@ -1,6 +1,7 @@
// ReSharper disable once CheckNamespace
namespace BD.WTTS.Columns;
[Mobius(Obsolete = true)]
public interface IReadOnlyName
{
string? Name { get; }
@@ -1,6 +1,8 @@
// ReSharper disable once CheckNamespace
namespace BD.WTTS.Columns;
[Mobius(Obsolete = true)]
[Obsolete]
public interface IReadOnlyStaticDisplayName
{
static abstract string? DisplayName { get; }
@@ -1,6 +1,10 @@
// ReSharper disable once CheckNamespace
namespace BD.WTTS;
[Mobius(
"""
CertificateConstants
""")]
public static class CertificateConstants
{
public const int CertificateValidDays = 300;
@@ -1,6 +1,10 @@
// ReSharper disable once CheckNamespace
namespace BD.WTTS;
[Mobius(
"""
Mobius.Constants.UrlConstants
""")]
[Obsolete("use Constants.Urls/Constants", true)]
public static class UrlConstants
{
@@ -4,11 +4,19 @@ namespace BD.WTTS.Diagnostics;
/// 启动耗时跟踪
/// </summary>
[Obsolete]
[Mobius(
"""
App.WriteElapsedMilliseconds
""", Obsolete = true)]
public static class StartWatchTrace
{
static Stopwatch? sw;
static StringBuilder? sb;
[Mobius(
"""
App.WriteElapsedMilliseconds
""")]
public static void Record(string? mark = null, bool dispose = false)
{
if (sw != null)
@@ -41,7 +49,12 @@ public static class StartWatchTrace
}
}
[Mobius(
"""
App.Elapsed
""")]
public static new string ToString() => sb?.ToString() ?? string.Empty;
[Mobius(Obsolete = true)]
public static long ElapsedMilliseconds => sw == null ? 0L : sw.ElapsedMilliseconds;
}
@@ -3,6 +3,7 @@ namespace BD.WTTS.Entities;
/// <summary>
/// 通知记录
/// </summary>
[Mobius(Obsolete = true)]
[SQLiteTable("A765AD32")]
[DebuggerDisplay("{DebuggerDisplay,nq}")]
public sealed class Notification : IEntity<Guid>
@@ -1,5 +1,9 @@
namespace BD.WTTS.Entities;
[Mobius(
"""
Mobius.Entities.Http.RequestCache
""")]
[SQLiteTable(TableName)]
[DebuggerDisplay("{DebuggerDisplay,nq}")]
public sealed class RequestCache : IEntity<string>
+4
View File
@@ -1,5 +1,9 @@
namespace BD.WTTS.Entities;
[Mobius(
"""
Mobius.Entities.Identity.User
""")]
[SQLiteTable("D5428AED")]
[DebuggerDisplay("{DebuggerDisplay,nq}")]
public sealed class User : IEntity<Guid>
+4
View File
@@ -3,6 +3,10 @@ namespace BD.WTTS.Enums;
/// <summary>
/// 应用程序终结点路由
/// </summary>
[Mobius(
"""
Mobius.Enums.App.AppEndPoint
""", Obsolete = true)]
public enum AppEndPoint : byte
{
LoginOrRegister = 1,
+2
View File
@@ -3,6 +3,7 @@ namespace BD.WTTS.Enums;
/// <summary>
/// 指定用于检索应用程序所使用的文件夹目录路径的枚举常数
/// </summary>
[Mobius(Obsolete = true)]
public enum AppFolder : byte
{
/// <summary>
@@ -24,6 +25,7 @@ public enum AppFolder : byte
/// <summary>
/// Enum 扩展 <see cref="AppFolder"/>
/// </summary>
[Mobius(Obsolete = true)]
public static class AppFolderEnumExtensions
{
/// <summary>
@@ -4,6 +4,10 @@ namespace BD.WTTS.Enums;
/// 应用服务级别,较低的级别注入较少的服务,适用于其他轻量进程业务启动
/// </summary>
[Flags]
[Mobius(
"""
Mobius.Enums.App.AppServicesLevel
""")]
public enum AppServicesLevel : uint
{
/// <summary>
+5
View File
@@ -5,6 +5,10 @@ namespace BD.WTTS.Enums;
/// <summary>
/// 应用程序主题
/// </summary>
[Mobius(
"""
Mobius.Enums.App.AppTheme
""")]
public enum AppTheme : byte
{
/// <summary>
@@ -34,6 +38,7 @@ public enum AppTheme : byte
Custom,
}
[Mobius(Obsolete = true)]
public static partial class AppThemeEnumExtensions
{
#if DEBUG
@@ -3,6 +3,10 @@ namespace BD.WTTS.Enums;
/// <summary>
/// 应用程序 Web 代理模式
/// </summary>
[Mobius(
"""
Mobius.Enums.App.AppWebProxyMode
""")]
public enum AppWebProxyMode : byte
{
/// <summary>
@@ -1,5 +1,10 @@
namespace BD.WTTS.Enums;
[Mobius(
"""
Mobius.Enums.ExitCode
// src/ExitCode.cs
""")]
public enum CommandExitCode
{
#region 100 ~ 5xx HttpStatusCode
+4
View File
@@ -1,5 +1,9 @@
namespace BD.WTTS.Enums;
[Mobius(
"""
Mobius.Enums.Text.EncodingType
""")]
public enum EncodingType : byte
{
/// <summary>
@@ -23,6 +23,10 @@ namespace BD.WTTS.Enums;
/// <para>在<see cref="NotificationType_Channel_EnumExtensions.GetDescription(NotificationChannelType)"/>中添加渠道描述</para>
/// <para>在<see cref="NotificationType_Channel_EnumExtensions.GetImportanceLevel(NotificationChannelType)"/>中添加渠道重要性级别</para>
/// </summary>
[Mobius(
"""
Mobius.Enums.PlatformNotification.PlatformNotificationChannelType
""")]
public enum NotificationChannelType
{
/// <inheritdoc cref="NotificationType.NewVersion"/>
@@ -45,6 +49,10 @@ public enum NotificationChannelType
/// <summary>
/// Enum 扩展 <see cref="NotificationType"/> AND <see cref="NotificationChannelType"/>
/// </summary>
[Mobius(
"""
Mobius.Enums.PlatformNotification.PlatformNotificationType_Channel_EnumExtensions
""")]
public static partial class NotificationType_Channel_EnumExtensions
{
/// <summary>
+4
View File
@@ -3,6 +3,10 @@ namespace BD.WTTS.Enums;
/// <summary>
/// 退出操作系统方式
/// </summary>
[Mobius(
"""
Mobius.Enums.App.OSExitMode
""")]
public enum OSExitMode : byte
{
/// <summary>
@@ -4,6 +4,7 @@ namespace BD.WTTS.Enums;
/// 表示操作结果的枚举
/// </summary>
[Description("操作结果的枚举")]
[Mobius(Obsolete = true)]
public enum OperationResultType
{
/// <summary>
@@ -3,6 +3,10 @@ namespace BD.WTTS.Enums;
/// <summary>
/// 文本阅读器提供商
/// </summary>
[Mobius(
"""
Mobius.Enums.Text.TextReaderProvider
""")]
public enum TextReaderProvider
{
/// <summary>
@@ -1,5 +1,9 @@
namespace BD.WTTS.Enums;
[Mobius(
"""
Mobius.Enums.UI.GridLayoutType
""")]
public enum GridLayoutType
{
List = 0,
@@ -1,5 +1,9 @@
namespace BD.WTTS.Enums;
[Mobius(
"""
Mobius.Enums.UI.NavigationTransitionEffect
""")]
public enum NavigationTransitionEffect
{
None = 0,
+4
View File
@@ -4,6 +4,10 @@ namespace BD.WTTS.Enums;
/// <summary>
/// 资源图标
/// </summary>
[Mobius(
"""
Mobius.Enums.UI.ResIcon
""")]
public enum ResIcon
{
None,
@@ -4,6 +4,10 @@ namespace BD.WTTS.Enums;
/// <summary>
/// 指定是否可以调整窗口大小,并在可调整大小的情况下指定如何调整窗口大小。由 SetResizeMode 使用
/// </summary>
[Mobius(
"""
Mobius.Enums.UI.ResizeMode
""")]
public enum ResizeMode : byte
{
/// <summary>
@@ -1,12 +1,14 @@
// ReSharper disable once CheckNamespace
namespace BD.WTTS.Enums;
[Mobius(
"""
Mobius.Enums.UI.WindowBackgroundMaterial
""")]
public enum WindowBackgroundMaterial : byte
{
None = 0,
Transparent = 1,
Blur = 2,
AcrylicBlur = 3,
@@ -1,6 +1,10 @@
// ReSharper disable once CheckNamespace
namespace BD.WTTS.Enums;
[Mobius(
"""
Mobius.Enums.UI.XamlMediaStretch
""")]
public enum XamlMediaStretch : byte
{
None = 0,
@@ -3,10 +3,18 @@ namespace BD.WTTS;
/// <summary>
/// <see cref="ApiRspImpl"/> 的扩展函数
/// </summary>
[Mobius(
"""
Mobius.ApiRspImplHandleUIExtensions
""", Obsolete = true)]
public static class ApiRspImplHandleUIExtensions
{
const string TAG = "HandleUI";
[Mobius(
"""
Mobius.ApiRspImplHandleUIExtensions
""", Obsolete = true)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static void LogError(this ApiRspBase apiRsp, string message)
{
@@ -22,6 +30,10 @@ public static class ApiRspImplHandleUIExtensions
/// <param name="errorAppendText"></param>
/// <param name="errorFormat"></param>
/// <returns></returns>
[Mobius(
"""
Mobius.ApiRspImplHandleUIExtensions
""", Obsolete = true)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool HandleUI(this ApiRspBase apiRsp, ToastIcon icon = ToastIcon.Error, string? errorAppendText = null, string? errorFormat = null)
{
@@ -52,6 +64,10 @@ public static class ApiRspImplHandleUIExtensions
/// <param name="errorAppendText"></param>
/// <param name="errorFormat"></param>
/// <returns></returns>
[Mobius(
"""
Mobius.ApiRspImplHandleUIExtensions
""", Obsolete = true)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool HandleUI<TContent>(this ApiRsp<TContent> apiRsp,
[NotNullWhen(true)] out TContent? content, ToastIcon icon = ToastIcon.Error, string? errorAppendText = null, string? errorFormat = null)
@@ -107,6 +123,10 @@ public static class ApiRspImplHandleUIExtensions
/// <param name="errorAppendText"></param>
/// <param name="errorFormat"></param>
/// <returns></returns>
[Mobius(
"""
Mobius.ApiRspImplHandleUIExtensions
""", Obsolete = true)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool HandleUIAllowNull<TContent>(this ApiRsp<TContent> apiRsp,
out TContent? content, ToastIcon icon = ToastIcon.Error, string? errorAppendText = null, string? errorFormat = null)
@@ -3,12 +3,15 @@ using System.Reactive.Disposables;
// ReSharper disable once CheckNamespace
namespace System;
[BD.Mobius(Obsolete = true)]
public static partial class CompositeDisposableExtensions
{
[BD.Mobius(Obsolete = true)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Add(this CompositeDisposable @this, Action dispose)
=> @this.Add(Disposable.Create(dispose));
[BD.Mobius(Obsolete = true)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Add(this CompositeDisposable @this, Func<ValueTask> dispose)
=> @this.Add(new AsyncDisposable(dispose));
@@ -7,6 +7,10 @@ namespace Microsoft.Extensions.DependencyInjection;
public static partial class ServiceCollectionExtensions
{
[BD.Mobius(
"""
Mobius.Helpers.*
""", Obsolete = true)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IServiceCollection AddPlatformService(
this IServiceCollection services,
@@ -4,6 +4,10 @@ namespace Microsoft.Extensions.DependencyInjection;
public static partial class ServiceCollectionExtensions
{
[BD.Mobius(
"""
Mobius.Helpers.WinScheduledTaskHelper
""", Obsolete = true)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IServiceCollection AddScheduledTaskService(this IServiceCollection services)
{
@@ -7,6 +7,7 @@ namespace System;
public static class ProcessExtensions
{
/// <inheritdoc cref="IPlatformService.GetCommandLineArgs(Process)"/>
[BD.Mobius(Obsolete = true)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string GetCommandLineArgs(this Process process)
{
@@ -8,6 +8,10 @@ public static partial class ServiceCollectionExtensions
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
[BD.Mobius(
"""
BD.Common8.Security.Extensions.AddSecurityService
""")]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IServiceCollection AddSecurityService(this IServiceCollection services)
{
@@ -10,6 +10,10 @@ public static partial class ServiceCollectionExtensions
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
[BD.Mobius(
"""
Mobius.Helpers.LogInitHelper.AddMobiusLogging
""")]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IServiceCollection AddGeneralLogging(this IServiceCollection services)
{
@@ -9,6 +9,10 @@ public static partial class ServiceCollectionExtensions
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
[BD.Mobius(
"""
services.AddSingleton<IHostsFileService, HostsFileServiceImpl>();
""", Obsolete = true)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IServiceCollection AddHostsFileService(this IServiceCollection services)
{
@@ -3,6 +3,11 @@ namespace Microsoft.Extensions.DependencyInjection;
public static partial class ServiceCollectionExtensions
{
[BD.Mobius(
"""
services.AddSingleton<IUserRepository, UserRepository>();
services.AddSingleton<IRequestCacheRepository, RequestCacheRepository>();
""", Obsolete = true)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IServiceCollection AddRepositories(this IServiceCollection services)
{
@@ -3,6 +3,10 @@ namespace Microsoft.Extensions.DependencyInjection;
public static partial class ServiceCollectionExtensions
{
[BD.Mobius(
"""
AddFrontendToast
""", Obsolete = true)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IServiceCollection AddStartupToastIntercept(this IServiceCollection services)
{
@@ -8,6 +8,7 @@ public static partial class ServiceCollectionExtensions
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
[BD.Mobius(Obsolete = true)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IServiceCollection AddViewModelManager(this IServiceCollection services)
{
@@ -8,6 +8,10 @@ public static partial class ServiceCollectionExtensions
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
[BD.Mobius(
"""
services.AddSingleton<IHttpPlatformHelperService, ClientHttpPlatformHelperServiceImpl>();
""", Obsolete = true)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IServiceCollection TryAddClientHttpPlatformHelperService(this IServiceCollection services)
{
@@ -3,6 +3,7 @@ namespace Microsoft.Extensions.DependencyInjection;
public static partial class ServiceCollectionExtensions
{
[BD.Mobius(Obsolete = true)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IServiceCollection TryAddFontManager(this IServiceCollection services)
{
@@ -8,6 +8,10 @@ public static partial class ServiceCollectionExtensions
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
[BD.Mobius(
"""
services.AddSingleton<IImageHttpClientService, ImageHttpClientServiceImpl>();
""", Obsolete = true)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IServiceCollection TryImageHttpClientService(this IServiceCollection services)
{
@@ -12,6 +12,10 @@ public static partial class ServiceCollectionExtensions
/// <param name="config"></param>
/// <param name="configureHandler"></param>
/// <returns></returns>
[BD.Mobius(
"""
services.AddSingleton<ISppWebApiService, SppWebApiServiceImpl>();
""", Obsolete = true)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IServiceCollection TryAddMicroServiceClient(
this IServiceCollection services)
@@ -8,6 +8,10 @@ public static partial class ServiceCollectionExtensions
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
[BD.Mobius(
"""
services.AddSingleton<INativeNotificationService, NativeNotificationServiceImpl>();
""", Obsolete = true)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IServiceCollection TryAddNotificationService(this IServiceCollection services)
{
@@ -8,6 +8,10 @@ public static partial class ServiceCollectionExtensions
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
[BD.Mobius(
"""
AddFrontendToast
""", Obsolete = true)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IServiceCollection TryAddToast(this IServiceCollection services)
=> ToastImpl.TryAddToast(services);
@@ -3,6 +3,10 @@ namespace Microsoft.Extensions.DependencyInjection;
public static partial class ServiceCollectionExtensions
{
[BD.Mobius(
"""
services.AddSingleton<IUserManager, BackendUserManager>();
""", Obsolete = true)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IServiceCollection TryAddUserManager(this IServiceCollection services)
{
@@ -3,8 +3,10 @@
// ReSharper disable once CheckNamespace
namespace BD.WTTS;
[Mobius(Obsolete = true)]
public static partial class CurrencyCodeEnumExtensions
{
[Mobius(Obsolete = true)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ECurrencyCode Convert(this CurrencyCode currencyCode)
{
@@ -13,6 +15,7 @@ public static partial class CurrencyCodeEnumExtensions
throw new ArgumentOutOfRangeException(nameof(currencyCode), currencyCode, null);
}
[Mobius(Obsolete = true)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static CurrencyCode Convert(this ECurrencyCode eCurrencyCode)
{
@@ -11,6 +11,10 @@ public static partial class ServiceCollectionExtensions
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
[BD.Mobius(
"""
services.AddSingleton<ISteamService, FrontendSteamService>();
""", Obsolete = true)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IServiceCollection AddSteamService2(this IServiceCollection services)
{
@@ -13,6 +13,10 @@ namespace BD.WTTS;
/// <item>Logs: $XDG_CACHE_HOME/Steam++/Logs or $HOME/.cache/Steam++/Logs</item>
/// </list>
/// </summary>
[Mobius(
"""
Mobius.Helpers
""")]
sealed class LinuxFileSystem : IOPath.FileSystemBase
{
private LinuxFileSystem() => throw new NotSupportedException();
@@ -9,6 +9,10 @@ namespace BD.WTTS;
/// <item>Logs: ~/Library/Caches/Steam++/Logs</item>
/// </list>
/// </summary>
[Mobius(
"""
Mobius.Helpers
""")]
sealed class MacCatalystFileSystem : IOPath.FileSystemBase
{
private MacCatalystFileSystem() => throw new NotSupportedException();
@@ -10,6 +10,10 @@ namespace BD.WTTS;
/// <item>Logs: \Logs or %Tmp%\Steam++\Logs</item>
/// </list>
/// </summary>
[Mobius(
"""
Mobius.Helpers
""")]
sealed class WindowsFileSystem : IOPath.FileSystemBase
{
private WindowsFileSystem() => throw new NotSupportedException();

Some files were not shown because too many files have changed in this diff Show More