mirror of
https://github.com/babalae/better-genshin-impact.git
synced 2026-05-15 09:17:13 +08:00
添加 HTTP 服务器配置和服务 (#1873)
This commit is contained in:
@@ -79,6 +79,9 @@ public partial class App : Application
|
||||
services.AddNavigationViewPageProvider();
|
||||
// App Host
|
||||
services.AddHostedService<ApplicationHostService>();
|
||||
// HTTP Server Service
|
||||
services.AddHostedService<HttpServerService>();
|
||||
|
||||
// Page resolver service
|
||||
services.AddSingleton<INavigationService, NavigationService>();
|
||||
services.AddSingleton<IUpdateService, UpdateService>();
|
||||
|
||||
@@ -49,6 +49,8 @@
|
||||
<PackageReference Include="Emoji.Wpf" Version="0.3.4" />
|
||||
<PackageReference Include="LibGit2Sharp" Version="0.31.0" />
|
||||
<PackageReference Include="Meziantou.Framework.Win32.CredentialManager" Version="1.7.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.App" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.2.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.4" />
|
||||
|
||||
@@ -223,6 +223,11 @@ public partial class AllConfig : ObservableObject
|
||||
/// </summary>
|
||||
public HardwareAccelerationConfig HardwareAccelerationConfig { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// HTTP 服务器配置
|
||||
/// </summary>
|
||||
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)
|
||||
|
||||
53
BetterGenshinImpact/Core/Config/HttpServerConfig.cs
Normal file
53
BetterGenshinImpact/Core/Config/HttpServerConfig.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using System;
|
||||
|
||||
namespace BetterGenshinImpact.Core.Config;
|
||||
|
||||
/// <summary>
|
||||
/// HTTP 服务器配置
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public partial class HttpServerConfig : ObservableObject
|
||||
{
|
||||
/// <summary>
|
||||
/// 是否启用 HTTP 服务器
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private bool _enabled = false;
|
||||
|
||||
/// <summary>
|
||||
/// HTTP 服务器端口
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private int _port = 30648;
|
||||
|
||||
/// <summary>
|
||||
/// 是否启用 CORS
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private bool _enableCors = true;
|
||||
|
||||
/// <summary>
|
||||
/// 是否启用 WebSocket 支持
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private bool _enableWebSocket = true;
|
||||
|
||||
/// <summary>
|
||||
/// API 访问令牌(可选,用于安全验证)
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private string _accessToken = Guid.NewGuid().ToString("N");
|
||||
|
||||
// /// <summary>
|
||||
// /// 是否启用 Swagger 文档
|
||||
// /// </summary>
|
||||
// [ObservableProperty]
|
||||
// private bool _enableSwagger = true;
|
||||
|
||||
/// <summary>
|
||||
/// 监听地址
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private string _host = "localhost";
|
||||
}
|
||||
@@ -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}"));
|
||||
}
|
||||
}
|
||||
244
BetterGenshinImpact/Service/HttpServerService.cs
Normal file
244
BetterGenshinImpact/Service/HttpServerService.cs
Normal file
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// HTTP 服务器服务
|
||||
/// </summary>
|
||||
public class HttpServerService : IHostedService, IDisposable
|
||||
{
|
||||
private readonly ILogger<HttpServerService> _logger;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private WebApplication? _webApp;
|
||||
private readonly HttpServerConfig _config;
|
||||
private CancellationTokenSource? _cancellationTokenSource;
|
||||
|
||||
public HttpServerService(
|
||||
ILogger<HttpServerService> 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<IConfigService>()!);
|
||||
|
||||
_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<BgiHub>("/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<object>() });
|
||||
})
|
||||
.WithTags("Scripts")
|
||||
.WithSummary("获取可用脚本列表");
|
||||
|
||||
// // 静态文件服务(可选)
|
||||
// app.MapGet("/", () => Results.Redirect("/swagger"));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_webApp?.DisposeAsync().AsTask().Wait();
|
||||
_cancellationTokenSource?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SignalR Hub,用于 WebSocket 通信
|
||||
/// </summary>
|
||||
public class BgiHub : Hub
|
||||
{
|
||||
private readonly ILogger<BgiHub> _logger;
|
||||
|
||||
public BgiHub(ILogger<BgiHub> 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 向所有连接的客户端发送消息
|
||||
/// </summary>
|
||||
public async Task BroadcastMessage(string message, object? data = null)
|
||||
{
|
||||
await Clients.Group("BgiClients").SendAsync("ReceiveMessage", new
|
||||
{
|
||||
message,
|
||||
data,
|
||||
timestamp = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -477,6 +477,142 @@
|
||||
<ui:ToggleSwitch Margin="0,0,36,0" IsChecked="{Binding Config.CommonConfig.ExitToTray, Mode=TwoWay}" />
|
||||
</ui:CardControl>
|
||||
|
||||
<!-- HTTP服务器设置 -->
|
||||
<ui:CardExpander Margin="0,0,0,12" ContentPadding="0" Icon="{ui:SymbolIcon Globe24}">
|
||||
<ui:CardExpander.Header>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:TextBlock Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
FontTypography="Body"
|
||||
Text="BetterGI HTTP 服务器设置"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBlock Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
|
||||
Text="可供外部程序调用,配置修改需重启后生效"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:ToggleSwitch Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="1"
|
||||
Margin="0,0,24,0"
|
||||
IsChecked="{Binding Config.HttpServerConfig.Enabled, Mode=TwoWay}" />
|
||||
</Grid>
|
||||
</ui:CardExpander.Header>
|
||||
<StackPanel>
|
||||
<Grid Margin="16">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:TextBlock Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
FontTypography="Body"
|
||||
Text="端口号"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBlock Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
|
||||
Text="HTTP服务器监听的端口号,范围1-65535"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBox Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="1"
|
||||
MinWidth="90"
|
||||
Margin="0,0,36,0"
|
||||
Text="{Binding Config.HttpServerConfig.Port, Mode=TwoWay,
|
||||
ValidatesOnNotifyDataErrors=True}" />
|
||||
</Grid>
|
||||
<!--<Grid Margin="16">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:TextBlock Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
FontTypography="Body"
|
||||
Text="启用跨域请求(CORS)"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBlock Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
|
||||
Text="允许来自不同域名的Web应用访问API"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:ToggleSwitch Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="1"
|
||||
Margin="0,0,36,0"
|
||||
IsChecked="{Binding Config.HttpServerConfig.EnableCors, Mode=TwoWay}" />
|
||||
</Grid>-->
|
||||
|
||||
<Grid Margin="16">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:TextBlock Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
FontTypography="Body"
|
||||
Text="API访问令牌"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBlock Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
|
||||
Text="可选的安全验证令牌,留空则不启用验证"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBox Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="1"
|
||||
MinWidth="170"
|
||||
Margin="0,0,36,0"
|
||||
Text="{Binding Config.HttpServerConfig.AccessToken, Mode=TwoWay}" />
|
||||
</Grid>
|
||||
<!--<Grid Margin="16">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:TextBlock Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
FontTypography="Body"
|
||||
Text="启用WebSocket支持"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBlock Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
|
||||
Text="启用WebSocket实时双向通信功能"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:ToggleSwitch Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="1"
|
||||
Margin="0,0,36,0"
|
||||
IsChecked="{Binding Config.HttpServerConfig.EnableWebSocket, Mode=TwoWay}" />
|
||||
</Grid>-->
|
||||
</StackPanel>
|
||||
</ui:CardExpander>
|
||||
|
||||
<ui:TextBlock Margin="0,0,0,8"
|
||||
FontTypography="BodyStrong"
|
||||
|
||||
Reference in New Issue
Block a user