Refine admin workflows and team access

This commit is contained in:
AzuTear
2026-06-26 18:09:29 +02:00
parent b7804e10ea
commit 8b69dfbafb
71 changed files with 5066 additions and 751 deletions
+55 -24
View File
@@ -4,6 +4,7 @@ using Backend.Data;
using Backend.Domain;
using Backend.Services;
using Microsoft.EntityFrameworkCore;
using Npgsql;
namespace Backend.Endpoints;
@@ -72,29 +73,7 @@ public static partial class PublicEndpoints
.FirstOrDefaultAsync(item => item.SeasonId == request.SeasonId && item.SubmittedByTwitchId == submitterId);
var isResubmission = ballot is not null;
if (ballot is null)
{
ballot = new VoteBallot
{
SeasonId = request.SeasonId,
SubmittedByTwitchId = submitterId,
};
await db.VoteBallots.AddAsync(ballot);
}
else
{
db.VoteEntries.RemoveRange(ballot.Entries);
ballot.Entries.Clear();
}
ballot.SubmittedAt = DateTimeOffset.UtcNow;
ballot.Status = "submitted";
ballot.Entries = request.Entries.Select(entry => new VoteEntry
{
CategoryId = entry.CategoryId,
CandidateId = entry.CandidateId,
}).ToList();
ballot = await ApplyVoteEntriesAsync(db, ballot, request.SeasonId, submitterId, request.Entries, context.RequestAborted);
var resubmittedBallotRule = await riskRuleService.GetRuleAsync("resubmitted_ballot", context.RequestAborted);
var rapidVoteUpdatesRule = await riskRuleService.GetRuleAsync("rapid_vote_updates", context.RequestAborted);
@@ -102,7 +81,21 @@ public static partial class PublicEndpoints
item.SubmittedByTwitchId == submitterId
&& item.SubmittedAt >= DateTimeOffset.UtcNow.AddMinutes(-rapidVoteUpdatesRule.WindowMinutes));
await db.SaveChangesAsync(context.RequestAborted);
try
{
await db.SaveChangesAsync(context.RequestAborted);
}
catch (DbUpdateException error) when (!isResubmission && IsUniqueVoteBallotViolation(error))
{
db.ChangeTracker.Clear();
ballot = await db.VoteBallots
.Include(item => item.Entries)
.FirstAsync(item => item.SeasonId == request.SeasonId && item.SubmittedByTwitchId == submitterId, context.RequestAborted);
isResubmission = true;
ballot = await ApplyVoteEntriesAsync(db, ballot, request.SeasonId, submitterId, request.Entries, context.RequestAborted);
await db.SaveChangesAsync(context.RequestAborted);
}
var ballotLink = new
{
label = "Voting-Analytics öffnen",
@@ -142,4 +135,42 @@ public static partial class PublicEndpoints
await db.SaveChangesAsync(context.RequestAborted);
return Results.Ok(new { ballotId = ballot.Id, entries = ballot.Entries.Count, updated = isResubmission });
}
private static async Task<VoteBallot> ApplyVoteEntriesAsync(
AwardsDbContext db,
VoteBallot? ballot,
int seasonId,
string submitterId,
VoteEntryRequest[] entries,
CancellationToken cancellationToken)
{
if (ballot is null)
{
ballot = new VoteBallot
{
SeasonId = seasonId,
SubmittedByTwitchId = submitterId,
};
await db.VoteBallots.AddAsync(ballot, cancellationToken);
}
else
{
db.VoteEntries.RemoveRange(ballot.Entries);
ballot.Entries.Clear();
}
ballot.SubmittedAt = DateTimeOffset.UtcNow;
ballot.Status = "submitted";
ballot.Entries = entries.Select(entry => new VoteEntry
{
CategoryId = entry.CategoryId,
CandidateId = entry.CandidateId,
}).ToList();
return ballot;
}
private static bool IsUniqueVoteBallotViolation(DbUpdateException error) =>
error.InnerException is PostgresException { SqlState: PostgresErrorCodes.UniqueViolation };
}