Files
RpgRoller/RpgRoller/Api/StateEventEndpoints.cs
2026-02-26 11:08:02 +01:00

57 lines
2.2 KiB
C#

using RpgRoller.Contracts;
using RpgRoller.Services;
namespace RpgRoller.Api;
internal static class StateEventEndpoints
{
public static RouteGroupBuilder MapStateEventEndpoints(this RouteGroupBuilder group)
{
group.MapGet("/events/state", async Task<IResult> (Guid campaignId, HttpContext context, IGameService game) =>
{
var sessionToken = context.GetRequiredSessionToken();
var versionResult = game.GetCampaignVersion(sessionToken, campaignId);
if (!versionResult.Succeeded)
{
return versionResult.Error!.Code == "unauthorized" ? TypedResults.Unauthorized() : TypedResults.BadRequest(new ApiError(versionResult.Error.Message));
}
context.Response.Headers.CacheControl = "no-cache";
context.Response.Headers.Connection = "keep-alive";
context.Response.ContentType = "text/event-stream";
var lastVersion = versionResult.Value;
await context.Response.WriteAsync($"event: state\ndata: {{\"campaignId\":\"{campaignId}\",\"version\":{lastVersion}}}\n\n");
await context.Response.Body.FlushAsync();
try
{
while (!context.RequestAborted.IsCancellationRequested)
{
await Task.Delay(TimeSpan.FromSeconds(1), context.RequestAborted);
var currentVersionResult = game.GetCampaignVersion(sessionToken, campaignId);
if (!currentVersionResult.Succeeded)
break;
if (currentVersionResult.Value != lastVersion)
{
lastVersion = currentVersionResult.Value;
await context.Response.WriteAsync($"event: state\ndata: {{\"campaignId\":\"{campaignId}\",\"version\":{lastVersion}}}\n\n");
}
else
await context.Response.WriteAsync(": heartbeat\n\n");
await context.Response.Body.FlushAsync();
}
}
catch (OperationCanceledException)
{
}
return TypedResults.Empty;
});
return group;
}
}