feat(backend): move endpoints to FastEndpoints handlers
This commit is contained in:
16
apps/backend/Handlers/GetWorldHandler.cs
Normal file
16
apps/backend/Handlers/GetWorldHandler.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using FastEndpoints;
|
||||||
|
using SpaceGame.Simulation.Api.Simulation;
|
||||||
|
|
||||||
|
namespace SpaceGame.Simulation.Api.Handlers;
|
||||||
|
|
||||||
|
public sealed class GetWorldHandler(WorldService worldService) : EndpointWithoutRequest
|
||||||
|
{
|
||||||
|
public override void Configure()
|
||||||
|
{
|
||||||
|
Get("/api/world");
|
||||||
|
AllowAnonymous();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Task HandleAsync(CancellationToken cancellationToken) =>
|
||||||
|
SendOkAsync(worldService.GetSnapshot(), cancellationToken);
|
||||||
|
}
|
||||||
24
apps/backend/Handlers/GetWorldHealthHandler.cs
Normal file
24
apps/backend/Handlers/GetWorldHealthHandler.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using FastEndpoints;
|
||||||
|
using SpaceGame.Simulation.Api.Simulation;
|
||||||
|
|
||||||
|
namespace SpaceGame.Simulation.Api.Handlers;
|
||||||
|
|
||||||
|
public sealed class GetWorldHealthHandler(WorldService worldService) : EndpointWithoutRequest
|
||||||
|
{
|
||||||
|
public override void Configure()
|
||||||
|
{
|
||||||
|
Get("/api/world/health");
|
||||||
|
AllowAnonymous();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Task HandleAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var status = worldService.GetStatus();
|
||||||
|
return SendOkAsync(new
|
||||||
|
{
|
||||||
|
ok = true,
|
||||||
|
sequence = status.Sequence,
|
||||||
|
generatedAtUtc = status.GeneratedAtUtc,
|
||||||
|
}, cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
16
apps/backend/Handlers/ResetWorldHandler.cs
Normal file
16
apps/backend/Handlers/ResetWorldHandler.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using FastEndpoints;
|
||||||
|
using SpaceGame.Simulation.Api.Simulation;
|
||||||
|
|
||||||
|
namespace SpaceGame.Simulation.Api.Handlers;
|
||||||
|
|
||||||
|
public sealed class ResetWorldHandler(WorldService worldService) : EndpointWithoutRequest
|
||||||
|
{
|
||||||
|
public override void Configure()
|
||||||
|
{
|
||||||
|
Post("/api/world/reset");
|
||||||
|
AllowAnonymous();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Task HandleAsync(CancellationToken cancellationToken) =>
|
||||||
|
SendOkAsync(worldService.Reset(), cancellationToken);
|
||||||
|
}
|
||||||
18
apps/backend/Handlers/RootRedirectHandler.cs
Normal file
18
apps/backend/Handlers/RootRedirectHandler.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using FastEndpoints;
|
||||||
|
|
||||||
|
namespace SpaceGame.Simulation.Api.Handlers;
|
||||||
|
|
||||||
|
public sealed class RootRedirectHandler : EndpointWithoutRequest
|
||||||
|
{
|
||||||
|
public override void Configure()
|
||||||
|
{
|
||||||
|
Get("/");
|
||||||
|
AllowAnonymous();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Task HandleAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
HttpContext.Response.Redirect("/api/world");
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
55
apps/backend/Handlers/StreamWorldHandler.cs
Normal file
55
apps/backend/Handlers/StreamWorldHandler.cs
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
using FastEndpoints;
|
||||||
|
using SpaceGame.Simulation.Api.Contracts;
|
||||||
|
using SpaceGame.Simulation.Api.Simulation;
|
||||||
|
|
||||||
|
namespace SpaceGame.Simulation.Api.Handlers;
|
||||||
|
|
||||||
|
public sealed class StreamWorldHandler(WorldService worldService) : EndpointWithoutRequest
|
||||||
|
{
|
||||||
|
private static readonly JsonSerializerOptions SseJsonOptions = new(JsonSerializerDefaults.Web);
|
||||||
|
|
||||||
|
public override void Configure()
|
||||||
|
{
|
||||||
|
Get("/api/world/stream");
|
||||||
|
AllowAnonymous();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task HandleAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
HttpContext.Response.Headers.Append("Cache-Control", "no-cache");
|
||||||
|
HttpContext.Response.Headers.Append("Content-Type", "text/event-stream");
|
||||||
|
|
||||||
|
var afterSequenceRaw = HttpContext.Request.Query["afterSequence"].ToString();
|
||||||
|
_ = long.TryParse(afterSequenceRaw, out var afterSequence);
|
||||||
|
|
||||||
|
var scopeKind = HttpContext.Request.Query["scopeKind"].ToString();
|
||||||
|
if (string.IsNullOrWhiteSpace(scopeKind))
|
||||||
|
{
|
||||||
|
scopeKind = HttpContext.Request.Query["scope"].ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(scopeKind))
|
||||||
|
{
|
||||||
|
scopeKind = "universe";
|
||||||
|
}
|
||||||
|
|
||||||
|
var systemId = HttpContext.Request.Query["systemId"].ToString();
|
||||||
|
var bubbleId = HttpContext.Request.Query["bubbleId"].ToString();
|
||||||
|
var scope = new ObserverScope(
|
||||||
|
scopeKind,
|
||||||
|
string.IsNullOrWhiteSpace(systemId) ? null : systemId,
|
||||||
|
string.IsNullOrWhiteSpace(bubbleId) ? null : bubbleId);
|
||||||
|
var stream = worldService.Subscribe(scope, afterSequence, cancellationToken);
|
||||||
|
|
||||||
|
await HttpContext.Response.WriteAsync(": connected\n\n", cancellationToken);
|
||||||
|
await HttpContext.Response.Body.FlushAsync(cancellationToken);
|
||||||
|
|
||||||
|
await foreach (var delta in stream.ReadAllAsync(cancellationToken))
|
||||||
|
{
|
||||||
|
var payload = JsonSerializer.Serialize(delta, SseJsonOptions);
|
||||||
|
await HttpContext.Response.WriteAsync($"event: world-delta\ndata: {payload}\n\n", cancellationToken);
|
||||||
|
await HttpContext.Response.Body.FlushAsync(cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
using SpaceGame.Simulation.Api.Contracts;
|
using FastEndpoints;
|
||||||
using SpaceGame.Simulation.Api.Simulation;
|
using SpaceGame.Simulation.Api.Simulation;
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
var sseJsonOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web);
|
|
||||||
|
|
||||||
builder.WebHost.UseUrls("http://127.0.0.1:5079");
|
builder.WebHost.UseUrls("http://127.0.0.1:5079");
|
||||||
builder.Services.AddCors((options) =>
|
builder.Services.AddCors((options) =>
|
||||||
@@ -18,61 +16,13 @@ builder.Services.AddCors((options) =>
|
|||||||
});
|
});
|
||||||
builder.Services.Configure<WorldGenerationOptions>(builder.Configuration.GetSection("WorldGeneration"));
|
builder.Services.Configure<WorldGenerationOptions>(builder.Configuration.GetSection("WorldGeneration"));
|
||||||
builder.Services.Configure<OrbitalSimulationOptions>(builder.Configuration.GetSection("OrbitalSimulation"));
|
builder.Services.Configure<OrbitalSimulationOptions>(builder.Configuration.GetSection("OrbitalSimulation"));
|
||||||
|
builder.Services.AddFastEndpoints();
|
||||||
builder.Services.AddSingleton<WorldService>();
|
builder.Services.AddSingleton<WorldService>();
|
||||||
builder.Services.AddHostedService<SimulationHostedService>();
|
builder.Services.AddHostedService<SimulationHostedService>();
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
app.UseCors();
|
app.UseCors();
|
||||||
|
app.UseFastEndpoints();
|
||||||
app.MapGet("/", () => Results.Redirect("/api/world"));
|
|
||||||
app.MapGet("/api/world", (WorldService worldService) => Results.Ok(worldService.GetSnapshot()));
|
|
||||||
app.MapGet("/api/world/stream", async (HttpContext httpContext, WorldService worldService, CancellationToken cancellationToken) =>
|
|
||||||
{
|
|
||||||
httpContext.Response.Headers.Append("Cache-Control", "no-cache");
|
|
||||||
httpContext.Response.Headers.Append("Content-Type", "text/event-stream");
|
|
||||||
|
|
||||||
var afterSequenceRaw = httpContext.Request.Query["afterSequence"].ToString();
|
|
||||||
_ = long.TryParse(afterSequenceRaw, out var afterSequence);
|
|
||||||
var scopeKind = httpContext.Request.Query["scopeKind"].ToString();
|
|
||||||
if (string.IsNullOrWhiteSpace(scopeKind))
|
|
||||||
{
|
|
||||||
scopeKind = httpContext.Request.Query["scope"].ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(scopeKind))
|
|
||||||
{
|
|
||||||
scopeKind = "universe";
|
|
||||||
}
|
|
||||||
|
|
||||||
var systemId = httpContext.Request.Query["systemId"].ToString();
|
|
||||||
var bubbleId = httpContext.Request.Query["bubbleId"].ToString();
|
|
||||||
var scope = new ObserverScope(
|
|
||||||
scopeKind,
|
|
||||||
string.IsNullOrWhiteSpace(systemId) ? null : systemId,
|
|
||||||
string.IsNullOrWhiteSpace(bubbleId) ? null : bubbleId);
|
|
||||||
var stream = worldService.Subscribe(scope, afterSequence, cancellationToken);
|
|
||||||
|
|
||||||
await httpContext.Response.WriteAsync(": connected\n\n", cancellationToken);
|
|
||||||
await httpContext.Response.Body.FlushAsync(cancellationToken);
|
|
||||||
|
|
||||||
await foreach (var delta in stream.ReadAllAsync(cancellationToken))
|
|
||||||
{
|
|
||||||
var payload = JsonSerializer.Serialize(delta, sseJsonOptions);
|
|
||||||
await httpContext.Response.WriteAsync($"event: world-delta\ndata: {payload}\n\n", cancellationToken);
|
|
||||||
await httpContext.Response.Body.FlushAsync(cancellationToken);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
app.MapGet("/api/world/health", (WorldService worldService) => Results.Ok(new
|
|
||||||
{
|
|
||||||
ok = true,
|
|
||||||
sequence = worldService.GetStatus().Sequence,
|
|
||||||
generatedAtUtc = worldService.GetStatus().GeneratedAtUtc,
|
|
||||||
}));
|
|
||||||
app.MapPost("/api/world/reset", (WorldService worldService) =>
|
|
||||||
{
|
|
||||||
var snapshot = worldService.Reset();
|
|
||||||
return Results.Ok(snapshot);
|
|
||||||
});
|
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
|
|||||||
@@ -6,4 +6,8 @@
|
|||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="FastEndpoints" Version="6.*" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
Reference in New Issue
Block a user