Harden request validation and admin UX cues
CI - Build & Verify / Build, Typecheck & Hygiene (push) Successful in 58s
CI - Build & Verify / Deploy to award.noveria.net (push) Successful in 1m24s

This commit is contained in:
AzuTear
2026-06-30 08:45:28 +02:00
parent 1191abc914
commit 33ba00a682
11 changed files with 248 additions and 70 deletions
+17 -10
View File
@@ -11,13 +11,20 @@ public static partial class PublicEndpoints
{
private static async Task<IResult> CreateClip(
HttpContext context,
CreateClipRequest request,
CreateClipRequest? request,
AwardsDbContext db,
IUserSessionService userSessionService,
IRiskFlagService riskFlagService,
IRiskRuleService riskRuleService)
{
if (!TryNormalizeExternalUrl(request.ClipUrl, out var clipUrl))
var validationError = ValidateCreateClipRequest(request);
if (validationError is not null)
{
return validationError;
}
var validatedRequest = request!;
if (!TryNormalizeExternalUrl(validatedRequest.ClipUrl, out var clipUrl))
{
return Results.BadRequest(new { message = "A valid http(s) clip link is required." });
}
@@ -44,7 +51,7 @@ public static partial class PublicEndpoints
return Results.BadRequest(new { message });
}
var season = await db.Seasons.FirstOrDefaultAsync(item => item.Year == request.Year);
var season = await db.Seasons.FirstOrDefaultAsync(item => item.Year == validatedRequest.Year);
var clipSeasonResolution = EnsurePublicWriteSeason(season, "nomination");
if (clipSeasonResolution.Result is not null)
{
@@ -53,26 +60,26 @@ public static partial class PublicEndpoints
season = clipSeasonResolution.Season!;
var selectedCandidate = request.CandidateId is int candidateId
var selectedCandidate = validatedRequest.CandidateId is int candidateId
? await db.Candidates
.AsNoTracking()
.FirstOrDefaultAsync(item => item.Id == candidateId && item.SeasonId == season.Id)
: null;
if (request.CandidateId is not null && selectedCandidate is null)
if (validatedRequest.CandidateId is not null && selectedCandidate is null)
{
return Results.BadRequest(new { message = "The selected candidate does not exist for this season." });
}
if (request.CategoryId is int requestedCategoryId
if (validatedRequest.CategoryId is int requestedCategoryId
&& selectedCandidate is not null
&& selectedCandidate.CategoryId != requestedCategoryId)
{
return Results.BadRequest(new { message = "The selected candidate does not belong to the selected category." });
}
var resolvedCategoryId = request.CategoryId ?? selectedCandidate?.CategoryId;
var normalizedTitle = request.Title?.Trim() ?? string.Empty;
var submittedCreator = request.Creator?.Trim();
var resolvedCategoryId = validatedRequest.CategoryId ?? selectedCandidate?.CategoryId;
var normalizedTitle = validatedRequest.Title?.Trim() ?? string.Empty;
var submittedCreator = validatedRequest.Creator?.Trim();
var normalizedCreator = string.IsNullOrWhiteSpace(submittedCreator)
? selectedCandidate?.DisplayName ?? string.Empty
: submittedCreator;
@@ -96,7 +103,7 @@ public static partial class PublicEndpoints
}
}
var submitterIdResult = await ResolveSubmitterIdAsync(context, request.TwitchUserId, userSessionService);
var submitterIdResult = await ResolveSubmitterIdAsync(context, validatedRequest.TwitchUserId, userSessionService);
if (submitterIdResult.Result is not null)
{
return submitterIdResult.Result;