Skip to content

Commit

Permalink
feat: 初步实现网页控制台
Browse files Browse the repository at this point in the history
  • Loading branch information
Zaitonn committed Jan 18, 2025
1 parent 9e17891 commit 654cc6f
Show file tree
Hide file tree
Showing 18 changed files with 298 additions and 64 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
using System.Text.Json.Serialization;

namespace Serein.Core.Models.Network.WebApi;

public class WebSocketBroadcastPacket(WebSocketBroadcastType type, string? data = null)
{
public string? Data { get; init; } = data;

[JsonConverter(typeof(JsonStringEnumConverter<WebSocketBroadcastType>))]
public WebSocketBroadcastType Type { get; init; } = type;
public string Type { get; init; } = type.ToString().ToLowerInvariant();
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ public enum WebSocketBroadcastType

Error,

Information,
Info,
}
2 changes: 1 addition & 1 deletion src/Serein.Core/Serein.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<InternalsVisibleTo Include="Serein.Tests" />
<InternalsVisibleTo Include="Serein.Plus" />

<PackageReference Include="DeepCloner" Version="0.10.4" />
<PackageReference Include="EmbedIO" Version="3.5.2" />
<PackageReference Include="Fleck" Version="1.2.0" />
<PackageReference Include="Hardware.Info" Version="101.0.0" />
Expand All @@ -30,5 +31,4 @@
<PackageReference Include="System.Text.Encoding.CodePages" Version="8.0.0" />
<PackageReference Include="WebSocket4Net" Version="0.15.2" />
</ItemGroup>

</Project>
2 changes: 1 addition & 1 deletion src/Serein.Core/SereinAppBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public static HostApplicationBuilder CreateBuilder()
.AddSingleton<HttpServer>()
.AddTransient<ApiMap>()
.AddTransient<IPBannerModule>()
.AddTransient<BroadcastWebSocketModule>()
.AddTransient<ServerWebSocketModule>()
.AddSingleton<PluginManager>()
.AddSingleton<EventDispatcher>()
.AddSingleton<JsEngineFactory>()
Expand Down
11 changes: 9 additions & 2 deletions src/Serein.Core/Services/Network/Connection/PacketHandler.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Nodes;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
Expand Down Expand Up @@ -43,13 +44,19 @@ public void Handle(JsonObject jsonObject)
case "message":
case "message_sent":
HandleMessagePacket(
jsonObject.ToObject<MessagePacket>(JsonSerializerOptionsFactory.PacketStyle)
JsonSerializer.Deserialize<MessagePacket>(
jsonObject,
JsonSerializerOptionsFactory.PacketStyle
)
);
break;

case "notice":
HandleNoticePacket(
jsonObject.ToObject<NoticePacket>(JsonSerializerOptionsFactory.PacketStyle)
JsonSerializer.Deserialize<NoticePacket>(
jsonObject,
JsonSerializerOptionsFactory.PacketStyle
)
);
break;
}
Expand Down
24 changes: 19 additions & 5 deletions src/Serein.Core/Services/Network/WebApi/Apis/ApiHelper.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Text.Json;
using System.Threading.Tasks;
using EmbedIO;
using Serein.Core.Models.Commands;
using Serein.Core.Models.Network.WebApi;
using Serein.Core.Utils;
using Serein.Core.Utils.Extensions;
Expand All @@ -12,7 +14,17 @@ namespace Serein.Core.Services.Network.WebApi.Apis;

public static class ApiHelper
{
public static async Task<T?> ConvertRequestAs<T>(this IHttpContext httpContext)
private static readonly JsonSerializerOptions Options =
new(JsonSerializerOptionsFactory.Common)
{
Converters =
{
new JsonObjectWithIdConverter<Match>(),
new JsonObjectWithIdConverter<Schedule>(),
},
};

public static async Task<T> ConvertRequestAs<T>(this IHttpContext httpContext)
where T : notnull
{
if (httpContext.Request.HttpVerb is not HttpVerbs.Get or HttpVerbs.Head)
Expand All @@ -24,10 +36,12 @@ public static class ApiHelper

try
{
return JsonSerializer.Deserialize<T>(
var value = JsonSerializer.Deserialize<T>(
await httpContext.GetRequestBodyAsStringAsync(),
JsonSerializerOptionsFactory.Common
);

return value is null ? throw HttpException.BadRequest("Body 不能为空") : value;
}
catch (Exception e)
{
Expand All @@ -45,7 +59,7 @@ ApiPacket<T> packet
{
httpContext.Response.StatusCode = packet.Code;
await httpContext.SendStringAsync(
JsonSerializer.Serialize(packet, JsonSerializerOptionsFactory.Common),
JsonSerializer.Serialize(packet, Options),
"text/json",
EncodingMap.UTF8
);
Expand Down Expand Up @@ -93,9 +107,9 @@ await context.SendPacketAsync(

public static async Task HandleException(IHttpContext context, Exception e)
{
if (e is InvalidOperationException ex)
if (e is InvalidOperationException)
{
await context.SendPacketAsync(new ApiPacket { ErrorMsg = ex.Message, Code = 403 });
await context.SendPacketAsync(new ApiPacket { ErrorMsg = e.Message, Code = 403 });
}
else
{
Expand Down
24 changes: 11 additions & 13 deletions src/Serein.Core/Services/Network/WebApi/Apis/AuthGate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,35 @@

namespace Serein.Core.Services.Network.WebApi.Apis;

internal class AuthGate(SettingProvider settingProvider) : WebModuleBase("/")
internal class AuthGate(SettingProvider settingProvider) : WebModuleBase("/api")
{
private readonly SettingProvider _settingProvider = settingProvider;

public override bool IsFinalHandler => false;

protected override Task OnRequestAsync(IHttpContext context)
protected override async Task OnRequestAsync(IHttpContext context)
{
if (context.RequestedPath == "/broadcast")
{
return Task.CompletedTask;
}
var auth = context.Request.Headers.Get("Authorization");

if (_settingProvider.Value.WebApi.AccessTokens.Length == 0)
{
return Task.CompletedTask;
return;
}

var auth = context.Request.Headers.Get("Authorization");

if (string.IsNullOrEmpty(auth))
{
throw HttpException.Unauthorized();
await ApiHelper.HandleHttpException(context, HttpException.Unauthorized());
return;
}

if (auth.StartsWith("Bearer "))
{
auth = auth[7..];
}

return !_settingProvider.Value.WebApi.AccessTokens.Contains(auth)
? throw HttpException.Unauthorized()
: Task.CompletedTask;
if (!_settingProvider.Value.WebApi.AccessTokens.Contains(auth))
{
await ApiHelper.HandleHttpException(context, HttpException.Unauthorized());
}
}
}
10 changes: 7 additions & 3 deletions src/Serein.Core/Services/Network/WebApi/Apis/MainApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,20 @@
namespace Serein.Core.Services.Network.WebApi.Apis;

internal partial class ApiMap(
HardwareInfoProvider hardwareInfoProvider,
ServerManager serverManager,
SettingProvider settingProvider,
WsConnectionManager wsConnectionManager
MatchesProvider matchesProvider,
ScheduleProvider scheduleProvider,
WsConnectionManager wsConnectionManager,
HardwareInfoProvider hardwareInfoProvider
) : WebApiController
{
private readonly HardwareInfoProvider _hardwareInfoProvider = hardwareInfoProvider;
private readonly ServerManager _serverManager = serverManager;
private readonly SettingProvider _settingProvider = settingProvider;
private readonly MatchesProvider _matchesProvider = matchesProvider;
private readonly ScheduleProvider _scheduleProvider = scheduleProvider;
private readonly WsConnectionManager _wsConnectionManager = wsConnectionManager;
private readonly HardwareInfoProvider _hardwareInfoProvider = hardwareInfoProvider;
private List<ApiEndpointRecord>? _records;

[Route(HttpVerbs.Get, "/metadata")]
Expand Down
61 changes: 61 additions & 0 deletions src/Serein.Core/Services/Network/WebApi/Apis/MatchApi.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using System.Linq;
using System.Threading.Tasks;
using EmbedIO;
using EmbedIO.Routing;
using Force.DeepCloner;
using Serein.Core.Models.Commands;

namespace Serein.Core.Services.Network.WebApi.Apis;

internal partial class ApiMap
{
[Route(HttpVerbs.Get, "/matches")]
public async Task GetMatches()
{
await HttpContext.SendPacketAsync(_matchesProvider.Value);
}

[Route(HttpVerbs.Post, "/matches")]
public async Task AddMatch()
{
var match = await HttpContext.ConvertRequestAs<Match>();
_matchesProvider.Value.Add(match);
_matchesProvider.SaveAsyncWithDebounce();

await HttpContext.SendPacketAsync(match.GetHashCode());
}

[Route(HttpVerbs.Delete, "/matches/{id}")]
public async Task DeleteMatch(int id)
{
var fisrt = _matchesProvider.Value.FirstOrDefault((m) => m.GetHashCode() == id);
if (fisrt is not null)
{
_matchesProvider.Value.Remove(fisrt);
_matchesProvider.SaveAsyncWithDebounce();
await HttpContext.SendPacketAsync();
}
else
{
throw HttpException.NotFound("未找到指定的匹配");
}
}

[Route(HttpVerbs.Put, "/matches/{id}")]
public async Task UpdateMatch(int id)
{
var match = await HttpContext.ConvertRequestAs<Match>();
var fisrt = _matchesProvider.Value.FirstOrDefault((m) => m.GetHashCode() == id);
if (fisrt is not null)
{
match.DeepCloneTo(fisrt);

_matchesProvider.SaveAsyncWithDebounce();
await HttpContext.SendPacketAsync(fisrt.GetHashCode());
}
else
{
throw HttpException.NotFound("未找到指定的匹配");
}
}
}
61 changes: 61 additions & 0 deletions src/Serein.Core/Services/Network/WebApi/Apis/ScheduleApi.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using System.Linq;
using System.Threading.Tasks;
using EmbedIO;
using EmbedIO.Routing;
using Force.DeepCloner;
using Serein.Core.Models.Commands;

namespace Serein.Core.Services.Network.WebApi.Apis;

internal partial class ApiMap
{
[Route(HttpVerbs.Get, "/schedules")]
public async Task GetSchedules()
{
await HttpContext.SendPacketAsync(_scheduleProvider.Value);
}

[Route(HttpVerbs.Post, "/schedules")]
public async Task CreateMatch()
{
var schedule = await HttpContext.ConvertRequestAs<Schedule>();
_scheduleProvider.Value.Add(schedule);
_scheduleProvider.SaveAsyncWithDebounce();

await HttpContext.SendPacketAsync(schedule.GetHashCode());
}

[Route(HttpVerbs.Delete, "/schedules/{id}")]
public async Task DeleteSchedules(int id)
{
var fisrt = _scheduleProvider.Value.FirstOrDefault((m) => m.GetHashCode() == id);
if (fisrt is not null)
{
_scheduleProvider.Value.Remove(fisrt);
_scheduleProvider.SaveAsyncWithDebounce();
await HttpContext.SendPacketAsync();
}
else
{
throw HttpException.NotFound("未找到指定的定时任务");
}
}

[Route(HttpVerbs.Put, "/schedules/{id}")]
public async Task UpdateSchedule(int id)
{
var schedule = await HttpContext.ConvertRequestAs<Schedule>();
var fisrt = _scheduleProvider.Value.FirstOrDefault((m) => m.GetHashCode() == id);
if (fisrt is not null)
{
schedule.DeepCloneTo(fisrt);

_scheduleProvider.SaveAsyncWithDebounce();
await HttpContext.SendPacketAsync(fisrt.GetHashCode());
}
else
{
throw HttpException.NotFound("未找到指定的匹配");
}
}
}
23 changes: 22 additions & 1 deletion src/Serein.Core/Services/Network/WebApi/Apis/ServersApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using EmbedIO;
using EmbedIO.Routing;
using EmbedIO.WebApi;
using Force.DeepCloner;
using Serein.Core.Models.Server;
using Serein.Core.Utils.Json;

Expand All @@ -26,12 +27,32 @@ public async Task AddServer(string id)
JsonSerializer.Deserialize<Configuration>(
jsonObject?["configuration"],
JsonSerializerOptionsFactory.Common
) ?? throw HttpException.BadRequest();
) ?? throw HttpException.BadRequest("请求中未包含有效的服务器配置");

_serverManager.Add(id, configuration);
await HttpContext.SendPacketAsync();
}

[Route(HttpVerbs.Put, "/servers/{id}")]
public async Task UpdateServer(string id)
{
var jsonObject = await HttpContext.ConvertRequestAs<JsonObject>();
var configuration =
JsonSerializer.Deserialize<Configuration>(
jsonObject?["configuration"],
JsonSerializerOptionsFactory.Common
) ?? throw HttpException.BadRequest("请求中未包含有效的服务器配置");

if (!_serverManager.Servers.TryGetValue(id, out var server))
{
throw HttpException.NotFound("未找到指定的服务器");
}

configuration.DeepCloneTo(server.Configuration);

await HttpContext.SendPacketAsync();
}

[Route(HttpVerbs.Delete, "/servers/{id}")]
public async Task RemoveServer(string id)
{
Expand Down
Loading

0 comments on commit 654cc6f

Please sign in to comment.