Revert "Implement admin back-pass flow and guarded admin actions"
This reverts commit 5595bfd3b1.
This commit is contained in:
@@ -17,23 +17,7 @@ public static class AdminEndpoints
|
||||
|
||||
admin.MapPost("/joker", async ([FromBody] GrantJokerRequest request, AdminWorkflowService service) => await service.GrantJokerAsync(request.PlayerId));
|
||||
|
||||
admin.MapPost("/players/{playerId:guid}/phase", async (Guid playerId, [FromBody] SetPlayerPhaseRequest request, HttpContext ctx, AppDbContext db, AdminWorkflowService service) =>
|
||||
{
|
||||
var player = await EndpointHelpers.GetAuthenticatedPlayer(ctx, db);
|
||||
if (player is null)
|
||||
return EndpointHelpers.UnauthorizedError();
|
||||
|
||||
return await service.SetPlayerPhaseAsync(playerId, request.Phase);
|
||||
});
|
||||
|
||||
admin.MapDelete("/players/{playerId:guid}", async (Guid playerId, [FromBody] AdminPasswordRequest request, HttpContext ctx, AppDbContext db, AdminWorkflowService service) =>
|
||||
{
|
||||
var player = await EndpointHelpers.GetAuthenticatedPlayer(ctx, db);
|
||||
if (player is null)
|
||||
return EndpointHelpers.UnauthorizedError();
|
||||
|
||||
return await service.DeletePlayerAsync(playerId, player.Id, request.AdminPassword);
|
||||
});
|
||||
admin.MapDelete("/players/{playerId:guid}", async (Guid playerId, AdminWorkflowService service) => await service.DeletePlayerAsync(playerId));
|
||||
|
||||
admin.MapPost("/link-suggestions", async ([FromBody] LinkSuggestionsRequest request, HttpContext ctx, AppDbContext db, AdminWorkflowService service) =>
|
||||
{
|
||||
@@ -53,23 +37,9 @@ public static class AdminEndpoints
|
||||
return await service.UnlinkSuggestionsAsync(player.Id, request.SuggestionId);
|
||||
});
|
||||
|
||||
admin.MapPost("/reset", async ([FromBody] AdminPasswordRequest request, HttpContext ctx, AppDbContext db, AdminWorkflowService service) =>
|
||||
{
|
||||
var player = await EndpointHelpers.GetAuthenticatedPlayer(ctx, db);
|
||||
if (player is null)
|
||||
return EndpointHelpers.UnauthorizedError();
|
||||
admin.MapPost("/reset", async (AdminWorkflowService service) => await service.ResetAsync());
|
||||
|
||||
return await service.ResetAsync(player.Id, request.AdminPassword);
|
||||
});
|
||||
|
||||
admin.MapPost("/factory-reset", async ([FromBody] AdminPasswordRequest request, HttpContext ctx, AppDbContext db, AdminWorkflowService service) =>
|
||||
{
|
||||
var player = await EndpointHelpers.GetAuthenticatedPlayer(ctx, db);
|
||||
if (player is null)
|
||||
return EndpointHelpers.UnauthorizedError();
|
||||
|
||||
return await service.FactoryResetAsync(player.Id, request.AdminPassword);
|
||||
});
|
||||
admin.MapPost("/factory-reset", async (AdminWorkflowService service) => await service.FactoryResetAsync());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using GameList.Contracts;
|
||||
using GameList.Data;
|
||||
using GameList.Domain;
|
||||
using GameList.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace GameList.Endpoints;
|
||||
@@ -22,21 +21,7 @@ internal sealed class AdminWorkflowService(AppDbContext db)
|
||||
}
|
||||
else
|
||||
{
|
||||
var playersWithSuggestions = await db.Suggestions.Select(s => s.PlayerId).Distinct().ToListAsync();
|
||||
if (playersWithSuggestions.Count == 0)
|
||||
{
|
||||
await db.Players.ExecuteUpdateAsync(p => p.SetProperty(x => x.CurrentPhase, Phase.Suggest).SetProperty(x => x.VotesFinal, false));
|
||||
}
|
||||
else
|
||||
{
|
||||
await db.Players
|
||||
.Where(p => playersWithSuggestions.Contains(p.Id))
|
||||
.ExecuteUpdateAsync(p => p.SetProperty(x => x.CurrentPhase, Phase.Vote).SetProperty(x => x.VotesFinal, false));
|
||||
|
||||
await db.Players
|
||||
.Where(p => !playersWithSuggestions.Contains(p.Id))
|
||||
.ExecuteUpdateAsync(p => p.SetProperty(x => x.CurrentPhase, Phase.Suggest).SetProperty(x => x.VotesFinal, false));
|
||||
}
|
||||
await db.Players.ExecuteUpdateAsync(p => p.SetProperty(x => x.CurrentPhase, Phase.Vote).SetProperty(x => x.VotesFinal, false));
|
||||
}
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
@@ -54,32 +39,11 @@ internal sealed class AdminWorkflowService(AppDbContext db)
|
||||
.Select(p => new VoteStatusDto(p.Id, p.DisplayName ?? p.Username, p.Username, p.CurrentPhase, p.VotesFinal, p.HasJoker, p.Suggestions.Count, p.Suggestions.Select(s => s.Name).ToList()))
|
||||
.ToListAsync();
|
||||
|
||||
var waiting = voters.Where(v => v.Phase == Phase.Vote && !v.Finalized).Select(v => v.Name).ToList();
|
||||
var waiting = voters.Where(v => !v.Finalized).Select(v => v.Name).ToList();
|
||||
var ready = waiting.Count == 0;
|
||||
return Results.Ok(new VoteStatusResponse(voters, ready, waiting));
|
||||
}
|
||||
|
||||
public async Task<IResult> SetPlayerPhaseAsync(Guid playerId, Phase phase)
|
||||
{
|
||||
if (phase != Phase.Suggest)
|
||||
return EndpointHelpers.BadRequestError("Players can only be moved back to the Suggest phase.");
|
||||
|
||||
var player = await db.Players.FirstOrDefaultAsync(p => p.Id == playerId);
|
||||
if (player is null)
|
||||
return EndpointHelpers.NotFoundError("Player not found.");
|
||||
|
||||
var current = await EndpointHelpers.GetCurrentPhaseAsync(db, player.Id);
|
||||
if (current != Phase.Vote)
|
||||
return EndpointHelpers.BadRequestError("Player must currently be in the Vote phase to move back.");
|
||||
|
||||
player.CurrentPhase = Phase.Suggest;
|
||||
player.VotesFinal = false;
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
var state = await db.AppState.AsNoTracking().SingleAsync();
|
||||
return Results.Ok(new PhaseTransitionResponse(player.CurrentPhase, state.ResultsOpen));
|
||||
}
|
||||
|
||||
public async Task<IResult> GrantJokerAsync(Guid playerId)
|
||||
{
|
||||
var player = await db.Players.FirstOrDefaultAsync(p => p.Id == playerId);
|
||||
@@ -97,12 +61,8 @@ internal sealed class AdminWorkflowService(AppDbContext db)
|
||||
return Results.Ok(new AdminGrantJokerResponse(player.Id, player.HasJoker));
|
||||
}
|
||||
|
||||
public async Task<IResult> DeletePlayerAsync(Guid playerId, Guid adminPlayerId, string? adminPassword)
|
||||
public async Task<IResult> DeletePlayerAsync(Guid playerId)
|
||||
{
|
||||
var passwordCheck = await ValidateAdminPasswordAsync(adminPlayerId, adminPassword);
|
||||
if (!passwordCheck.IsValid)
|
||||
return passwordCheck.Error!;
|
||||
|
||||
var player = await db.Players.Include(p => p.Suggestions).FirstOrDefaultAsync(p => p.Id == playerId);
|
||||
if (player is null)
|
||||
return EndpointHelpers.NotFoundError("Player not found.");
|
||||
@@ -218,12 +178,8 @@ internal sealed class AdminWorkflowService(AppDbContext db)
|
||||
return Results.Ok(new AdminUnlinkSuggestionsResponse(groupIds, await db.Players.CountAsync()));
|
||||
}
|
||||
|
||||
public async Task<IResult> ResetAsync(Guid adminPlayerId, string? adminPassword)
|
||||
public async Task<IResult> ResetAsync()
|
||||
{
|
||||
var passwordCheck = await ValidateAdminPasswordAsync(adminPlayerId, adminPassword);
|
||||
if (!passwordCheck.IsValid)
|
||||
return passwordCheck.Error!;
|
||||
|
||||
await using var tx = await db.Database.BeginTransactionAsync();
|
||||
|
||||
await db.Votes.ExecuteDeleteAsync();
|
||||
@@ -239,12 +195,8 @@ internal sealed class AdminWorkflowService(AppDbContext db)
|
||||
return Results.Ok(new AdminResetStateResponse(Phase.Suggest, state.ResultsOpen, state.UpdatedAt));
|
||||
}
|
||||
|
||||
public async Task<IResult> FactoryResetAsync(Guid adminPlayerId, string? adminPassword)
|
||||
public async Task<IResult> FactoryResetAsync()
|
||||
{
|
||||
var passwordCheck = await ValidateAdminPasswordAsync(adminPlayerId, adminPassword);
|
||||
if (!passwordCheck.IsValid)
|
||||
return passwordCheck.Error!;
|
||||
|
||||
await using var tx = await db.Database.BeginTransactionAsync();
|
||||
|
||||
await db.Votes.ExecuteDeleteAsync();
|
||||
@@ -260,19 +212,4 @@ internal sealed class AdminWorkflowService(AppDbContext db)
|
||||
|
||||
return Results.Ok(new AdminResetStateResponse(Phase.Suggest, fresh.ResultsOpen, fresh.UpdatedAt));
|
||||
}
|
||||
|
||||
private async Task<(bool IsValid, IResult? Error)> ValidateAdminPasswordAsync(Guid adminPlayerId, string? adminPassword)
|
||||
{
|
||||
if (string.IsNullOrEmpty(adminPassword))
|
||||
return (false, EndpointHelpers.BadRequestError("Admin password is required."));
|
||||
|
||||
var admin = await db.Players.AsNoTracking().FirstOrDefaultAsync(p => p.Id == adminPlayerId);
|
||||
if (admin is null)
|
||||
return (false, EndpointHelpers.UnauthorizedError());
|
||||
|
||||
if (!PasswordHasher.Verify(adminPassword, admin.PasswordHash, admin.PasswordSalt))
|
||||
return (false, EndpointHelpers.UnauthorizedError("Invalid admin password."));
|
||||
|
||||
return (true, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,32 +72,15 @@ internal sealed class StateWorkflowService(AppDbContext db)
|
||||
|
||||
public async Task<IResult> PrevPhaseAsync(Player player)
|
||||
{
|
||||
var appState = await db.AppState.SingleAsync();
|
||||
var shouldSave = EndpointHelpers.ReconcilePlayerPhase(player, appState.ResultsOpen);
|
||||
|
||||
if (!player.IsAdmin)
|
||||
{
|
||||
if (player.CurrentPhase != Phase.Vote)
|
||||
return EndpointHelpers.BadRequestError("You can only move back from the Vote phase.");
|
||||
return EndpointHelpers.BadRequestError("Only admins can move backward.");
|
||||
|
||||
if (!player.HasJoker)
|
||||
return EndpointHelpers.BadRequestError("Only admins can move backward.");
|
||||
|
||||
player.CurrentPhase = Phase.Suggest;
|
||||
player.VotesFinal = false;
|
||||
player.HasJoker = false;
|
||||
shouldSave = true;
|
||||
await db.SaveChangesAsync();
|
||||
return Results.Ok(new PhaseTransitionResponse(player.CurrentPhase, appState.ResultsOpen));
|
||||
}
|
||||
var appState = await db.AppState.SingleAsync();
|
||||
_ = EndpointHelpers.ReconcilePlayerPhase(player, appState.ResultsOpen);
|
||||
|
||||
player.CurrentPhase = PrevPhase(player.CurrentPhase);
|
||||
player.VotesFinal = false;
|
||||
shouldSave = true;
|
||||
|
||||
if (shouldSave)
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
return Results.Ok(new PhaseTransitionResponse(player.CurrentPhase, appState.ResultsOpen));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user