From daa4aea7a85d449372a2c845e18e8bfd2711fafe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BE=89=E9=B8=AD=E8=9B=8B?= Date: Mon, 14 Jul 2025 00:58:24 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20HTTP=20=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E5=99=A8=E9=85=8D=E7=BD=AE=E5=92=8C=E6=9C=8D=E5=8A=A1=20(#1873?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BetterGenshinImpact/App.xaml.cs | 3 + .../BetterGenshinImpact.csproj | 2 + BetterGenshinImpact/Core/Config/AllConfig.cs | 6 + .../Core/Config/HttpServerConfig.cs | 53 ++++ .../Helpers/Win32/MirrorChyanHelper.cs | 2 +- .../Service/HttpServerService.cs | 244 ++++++++++++++++++ .../View/Pages/CommonSettingsPage.xaml | 136 ++++++++++ 7 files changed, 445 insertions(+), 1 deletion(-) create mode 100644 BetterGenshinImpact/Core/Config/HttpServerConfig.cs create mode 100644 BetterGenshinImpact/Service/HttpServerService.cs diff --git a/BetterGenshinImpact/App.xaml.cs b/BetterGenshinImpact/App.xaml.cs index abf005e1..1ef1b403 100644 --- a/BetterGenshinImpact/App.xaml.cs +++ b/BetterGenshinImpact/App.xaml.cs @@ -79,6 +79,9 @@ public partial class App : Application services.AddNavigationViewPageProvider(); // App Host services.AddHostedService(); + // HTTP Server Service + services.AddHostedService(); + // Page resolver service services.AddSingleton(); services.AddSingleton(); diff --git a/BetterGenshinImpact/BetterGenshinImpact.csproj b/BetterGenshinImpact/BetterGenshinImpact.csproj index 1dd666f2..27cca97d 100644 --- a/BetterGenshinImpact/BetterGenshinImpact.csproj +++ b/BetterGenshinImpact/BetterGenshinImpact.csproj @@ -49,6 +49,8 @@ + + diff --git a/BetterGenshinImpact/Core/Config/AllConfig.cs b/BetterGenshinImpact/Core/Config/AllConfig.cs index d1624e3b..e53d122d 100644 --- a/BetterGenshinImpact/Core/Config/AllConfig.cs +++ b/BetterGenshinImpact/Core/Config/AllConfig.cs @@ -223,6 +223,11 @@ public partial class AllConfig : ObservableObject /// public HardwareAccelerationConfig HardwareAccelerationConfig { get; set; } = new(); + /// + /// HTTP 服务器配置 + /// + public HttpServerConfig HttpServerConfig { get; set; } = new(); + [JsonIgnore] public Action? OnAnyChangedAction { get; set; } @@ -253,6 +258,7 @@ public partial class AllConfig : ObservableObject PathingConditionConfig.PropertyChanged += OnAnyPropertyChanged; DevConfig.PropertyChanged += OnAnyPropertyChanged; HardwareAccelerationConfig.PropertyChanged += OnAnyPropertyChanged; + HttpServerConfig.PropertyChanged += OnAnyPropertyChanged; } public void OnAnyPropertyChanged(object? sender, EventArgs args) diff --git a/BetterGenshinImpact/Core/Config/HttpServerConfig.cs b/BetterGenshinImpact/Core/Config/HttpServerConfig.cs new file mode 100644 index 00000000..a38ffc62 --- /dev/null +++ b/BetterGenshinImpact/Core/Config/HttpServerConfig.cs @@ -0,0 +1,53 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using System; + +namespace BetterGenshinImpact.Core.Config; + +/// +/// HTTP 服务器配置 +/// +[Serializable] +public partial class HttpServerConfig : ObservableObject +{ + /// + /// 是否启用 HTTP 服务器 + /// + [ObservableProperty] + private bool _enabled = false; + + /// + /// HTTP 服务器端口 + /// + [ObservableProperty] + private int _port = 30648; + + /// + /// 是否启用 CORS + /// + [ObservableProperty] + private bool _enableCors = true; + + /// + /// 是否启用 WebSocket 支持 + /// + [ObservableProperty] + private bool _enableWebSocket = true; + + /// + /// API 访问令牌(可选,用于安全验证) + /// + [ObservableProperty] + private string _accessToken = Guid.NewGuid().ToString("N"); + + // /// + // /// 是否启用 Swagger 文档 + // /// + // [ObservableProperty] + // private bool _enableSwagger = true; + + /// + /// 监听地址 + /// + [ObservableProperty] + private string _host = "localhost"; +} \ No newline at end of file diff --git a/BetterGenshinImpact/Helpers/Win32/MirrorChyanHelper.cs b/BetterGenshinImpact/Helpers/Win32/MirrorChyanHelper.cs index 1f22f189..624dd474 100644 --- a/BetterGenshinImpact/Helpers/Win32/MirrorChyanHelper.cs +++ b/BetterGenshinImpact/Helpers/Win32/MirrorChyanHelper.cs @@ -95,6 +95,6 @@ public static class MirrorChyanHelper private static void OpenMirrorChyanWebsite() { - Launcher.LaunchUriAsync(new Uri($"https://mirrorchyan.com/zh/get-start?source=bgi-{Global.Version}")); + Launcher.LaunchUriAsync(new Uri($"https://mirrorchyan.com/zh/get-start?source=bgi-desktop-{Global.Version}")); } } \ No newline at end of file diff --git a/BetterGenshinImpact/Service/HttpServerService.cs b/BetterGenshinImpact/Service/HttpServerService.cs new file mode 100644 index 00000000..84f640bd --- /dev/null +++ b/BetterGenshinImpact/Service/HttpServerService.cs @@ -0,0 +1,244 @@ +using BetterGenshinImpact.Core.Config; +using BetterGenshinImpact.GameTask; +using BetterGenshinImpact.Service.Interface; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.SignalR; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; + +namespace BetterGenshinImpact.Service; + +/// +/// HTTP 服务器服务 +/// +public class HttpServerService : IHostedService, IDisposable +{ + private readonly ILogger _logger; + private readonly IServiceProvider _serviceProvider; + private WebApplication? _webApp; + private readonly HttpServerConfig _config; + private CancellationTokenSource? _cancellationTokenSource; + + public HttpServerService( + ILogger logger, + IConfigService configService, + IServiceProvider serviceProvider) + { + _logger = logger; + _serviceProvider = serviceProvider; + _config = configService.Get().HttpServerConfig; + } + + public async Task StartAsync(CancellationToken cancellationToken) + { + if (!_config.Enabled) + { + _logger.LogInformation("HTTP 服务器未启用"); + return; + } + + _cancellationTokenSource = new CancellationTokenSource(); + await StartWebServer(); + } + + public async Task StopAsync(CancellationToken cancellationToken) + { + if (_webApp != null) + { + await _webApp.StopAsync(cancellationToken); + await _webApp.DisposeAsync(); + _webApp = null; + } + + _cancellationTokenSource?.Cancel(); + _cancellationTokenSource?.Dispose(); + _logger.LogInformation("HTTP 服务器已停止"); + } + + private async Task StartWebServer() + { + try + { + var builder = WebApplication.CreateBuilder(); + + // 配置 Kestrel 服务器 + builder.WebHost.ConfigureKestrel(options => + { + options.ListenAnyIP(_config!.Port); + }); + + // 添加服务 + builder.Services.AddCors(); + + if (_config!.EnableWebSocket) + { + builder.Services.AddSignalR(); + } + + // if (_config.EnableSwagger) + // { + // builder.Services.AddEndpointsApiExplorer(); + // // builder.Services.AddSwaggerGen(); + // } + + // 添加现有服务的引用 + builder.Services.AddSingleton(_serviceProvider.GetService()!); + + _webApp = builder.Build(); + + // 配置中间件 + if (_config.EnableCors) + { + _webApp.UseCors(policy => + { + policy.AllowAnyOrigin() + .AllowAnyMethod() + .AllowAnyHeader(); + }); + } + + // if (_config.EnableSwagger) + // { + // // _webApp.UseSwagger(); + // // _webApp.UseSwaggerUI(); + // } + + // 配置路由 + ConfigureRoutes(_webApp); + + if (_config.EnableWebSocket) + { + _webApp.MapHub("/bgi-hub"); + } + + // 启动服务器 + await _webApp.StartAsync(_cancellationTokenSource!.Token); + + var url = $"http://{_config.Host}:{_config.Port}"; + _logger.LogInformation("HTTP 服务器已启动: {Url}", url); + } + catch (Exception ex) + { + _logger.LogError(ex, "启动 HTTP 服务器失败"); + throw; + } + } + + private void ConfigureRoutes(WebApplication app) + { + // 健康检查 + app.MapGet("/health", () => new { status = "healthy", timestamp = DateTime.UtcNow }) + .WithTags("System") + .WithSummary("健康检查"); + + // 获取应用状态 + app.MapGet("/api/status", () => + { + }) + .WithTags("Application") + .WithSummary("获取应用状态"); + + // 获取配置 + app.MapGet("/api/config", (IConfigService configService) => + { + var config = configService.Get(); + return Results.Json(config, ConfigService.JsonOptions); + }) + .WithTags("Configuration") + .WithSummary("获取应用配置"); + + // 更新配置(仅部分配置) + app.MapPost("/api/config", async (HttpContext context, IConfigService configService) => + { + try + { + var jsonDoc = await JsonDocument.ParseAsync(context.Request.Body); + var config = configService.Get(); + configService.Save(); + return Results.Ok(new { message = "配置更新成功" }); + } + catch (Exception ex) + { + return Results.BadRequest(new { error = ex.Message }); + } + }) + .WithTags("Configuration") + .WithSummary("更新应用配置"); + + // 启动/停止任务 + app.MapPost("/api/tasks/{action}", (string action) => + { + + }) + .WithTags("Tasks") + .WithSummary("启动或停止任务"); + + // 获取任务列表(脚本) + app.MapGet("/api/scripts", () => + { + // 这里可以返回可用的脚本列表 + // 具体实现根据你的脚本管理逻辑 + return Results.Ok(new { scripts = new List() }); + }) + .WithTags("Scripts") + .WithSummary("获取可用脚本列表"); + + // // 静态文件服务(可选) + // app.MapGet("/", () => Results.Redirect("/swagger")); + } + + public void Dispose() + { + _webApp?.DisposeAsync().AsTask().Wait(); + _cancellationTokenSource?.Dispose(); + } +} + +/// +/// SignalR Hub,用于 WebSocket 通信 +/// +public class BgiHub : Hub +{ + private readonly ILogger _logger; + + public BgiHub(ILogger logger) + { + _logger = logger; + } + + public override async Task OnConnectedAsync() + { + _logger.LogInformation("WebSocket 客户端已连接: {ConnectionId}", Context.ConnectionId); + await Groups.AddToGroupAsync(Context.ConnectionId, "BgiClients"); + await base.OnConnectedAsync(); + } + + public override async Task OnDisconnectedAsync(Exception? exception) + { + _logger.LogInformation("WebSocket 客户端已断开: {ConnectionId}", Context.ConnectionId); + await Groups.RemoveFromGroupAsync(Context.ConnectionId, "BgiClients"); + await base.OnDisconnectedAsync(exception); + } + + /// + /// 向所有连接的客户端发送消息 + /// + public async Task BroadcastMessage(string message, object? data = null) + { + await Clients.Group("BgiClients").SendAsync("ReceiveMessage", new + { + message, + data, + timestamp = DateTime.UtcNow + }); + } +} \ No newline at end of file diff --git a/BetterGenshinImpact/View/Pages/CommonSettingsPage.xaml b/BetterGenshinImpact/View/Pages/CommonSettingsPage.xaml index 10bc5280..6f5e1496 100644 --- a/BetterGenshinImpact/View/Pages/CommonSettingsPage.xaml +++ b/BetterGenshinImpact/View/Pages/CommonSettingsPage.xaml @@ -477,6 +477,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +