Code cleanup

This commit is contained in:
2026-02-26 11:08:02 +01:00
parent 9036a3a157
commit e7114d8798
72 changed files with 1069 additions and 1604 deletions

View File

@@ -8,8 +8,7 @@ public static class ApiEndpointRegistration
api.MapSystemEndpoints();
api.MapAuthEndpoints();
var authenticatedApi = api.MapGroup(string.Empty)
.AddEndpointFilter<RequireSessionTokenFilter>();
var authenticatedApi = api.MapGroup(string.Empty).AddEndpointFilter<RequireSessionTokenFilter>();
authenticatedApi.MapMeEndpoints();
authenticatedApi.MapCampaignEndpoints();
@@ -17,4 +16,4 @@ public static class ApiEndpointRegistration
authenticatedApi.MapSkillEndpoints();
authenticatedApi.MapStateEventEndpoints();
}
}
}

View File

@@ -9,14 +9,10 @@ internal static class ApiResultMapper
public static Results<Ok<T>, BadRequest<ApiError>, UnauthorizedHttpResult> ToApiResult<T>(ServiceResult<T> result)
{
if (result.Succeeded)
{
return TypedResults.Ok(result.Value!);
}
if (result.Error!.Code == "unauthorized")
{
return TypedResults.Unauthorized();
}
return TypedResults.BadRequest(new ApiError(result.Error.Message));
}
@@ -25,4 +21,4 @@ internal static class ApiResultMapper
{
return TypedResults.BadRequest(new ApiError(error.Message));
}
}
}

View File

@@ -12,9 +12,7 @@ internal static class AuthEndpoints
{
var result = game.Register(request.Username, request.Password, request.DisplayName);
if (!result.Succeeded)
{
return ApiResultMapper.ToBadRequest(result.Error!);
}
return TypedResults.Ok(result.Value!);
});
@@ -23,11 +21,9 @@ internal static class AuthEndpoints
{
var result = game.Login(request.Username, request.Password);
if (!result.Succeeded)
{
return ApiResultMapper.ToBadRequest(result.Error!);
}
context.Response.Cookies.Append(SessionCookie.Name, result.Value.SessionToken, new CookieOptions
context.Response.Cookies.Append(SessionCookie.Name, result.Value.SessionToken, new()
{
HttpOnly = true,
SameSite = SameSiteMode.Strict,
@@ -41,9 +37,7 @@ internal static class AuthEndpoints
group.MapPost("/auth/logout", (HttpContext context, IGameService game) =>
{
if (context.TryReadSessionTokenFromCookie(out var sessionToken))
{
game.Logout(sessionToken);
}
context.Response.Cookies.Delete(SessionCookie.Name);
return TypedResults.NoContent();
@@ -51,4 +45,4 @@ internal static class AuthEndpoints
return group;
}
}
}

View File

@@ -33,4 +33,4 @@ internal static class CampaignEndpoints
return group;
}
}
}

View File

@@ -33,4 +33,4 @@ internal static class CharacterEndpoints
return group;
}
}
}

View File

@@ -16,4 +16,4 @@ internal static class MeEndpoints
return group;
}
}
}

View File

@@ -2,4 +2,4 @@ namespace RpgRoller.Api;
internal static class RequestMappings
{
}
}

View File

@@ -5,11 +5,9 @@ internal sealed class RequireSessionTokenFilter : IEndpointFilter
public ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next)
{
if (!context.HttpContext.TryReadSessionTokenFromCookie(out var sessionToken))
{
return ValueTask.FromResult<object?>(TypedResults.Unauthorized());
}
context.HttpContext.StoreSessionToken(sessionToken);
return next(context);
}
}
}

View File

@@ -3,4 +3,4 @@ namespace RpgRoller.Api;
internal static class SessionCookie
{
public const string Name = "rpgroller_session";
}
}

View File

@@ -2,8 +2,6 @@ namespace RpgRoller.Api;
internal static class SessionTokenHttpContextExtensions
{
private const string SessionTokenItemKey = "__rpgroller.session-token";
public static bool TryReadSessionTokenFromCookie(this HttpContext context, out string sessionToken)
{
sessionToken = context.Request.Cookies[SessionCookie.Name] ?? string.Empty;
@@ -17,13 +15,11 @@ internal static class SessionTokenHttpContextExtensions
public static string GetRequiredSessionToken(this HttpContext context)
{
if (context.Items.TryGetValue(SessionTokenItemKey, out var token)
&& token is string sessionToken
&& !string.IsNullOrWhiteSpace(sessionToken))
{
if (context.Items.TryGetValue(SessionTokenItemKey, out var token) && token is string sessionToken && !string.IsNullOrWhiteSpace(sessionToken))
return sessionToken;
}
throw new InvalidOperationException("Session token is not available in this request.");
}
}
private const string SessionTokenItemKey = "__rpgroller.session-token";
}

View File

@@ -27,4 +27,4 @@ internal static class SkillEndpoints
return group;
}
}
}

View File

@@ -7,18 +7,13 @@ internal static class StateEventEndpoints
{
public static RouteGroupBuilder MapStateEventEndpoints(this RouteGroupBuilder group)
{
group.MapGet("/events/state", async Task<IResult> (
Guid campaignId,
HttpContext context,
IGameService game) =>
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));
return versionResult.Error!.Code == "unauthorized" ? TypedResults.Unauthorized() : TypedResults.BadRequest(new ApiError(versionResult.Error.Message));
}
context.Response.Headers.CacheControl = "no-cache";
@@ -37,9 +32,7 @@ internal static class StateEventEndpoints
var currentVersionResult = game.GetCampaignVersion(sessionToken, campaignId);
if (!currentVersionResult.Succeeded)
{
break;
}
if (currentVersionResult.Value != lastVersion)
{
@@ -47,9 +40,7 @@ internal static class StateEventEndpoints
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();
}
@@ -63,4 +54,4 @@ internal static class StateEventEndpoints
return group;
}
}
}

View File

@@ -11,4 +11,4 @@ internal static class SystemEndpoints
group.MapGet("/rulesets", (IGameService game) => TypedResults.Ok(game.GetRulesets()));
return group;
}
}
}