diff --git a/Foxnouns.Backend/Controllers/FlagsController.cs b/Foxnouns.Backend/Controllers/FlagsController.cs index 31f3400..5021d8e 100644 --- a/Foxnouns.Backend/Controllers/FlagsController.cs +++ b/Foxnouns.Backend/Controllers/FlagsController.cs @@ -5,6 +5,7 @@ using Foxnouns.Backend.Extensions; using Foxnouns.Backend.Jobs; using Foxnouns.Backend.Middleware; using Foxnouns.Backend.Services; +using Foxnouns.Backend.Utils; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; @@ -20,7 +21,7 @@ public class FlagsController( IQueue queue) : ApiControllerBase { private readonly ILogger _logger = logger.ForContext(); - + [HttpGet] [Authorize("identify")] [ProducesResponseType>(statusCode: StatusCodes.Status200OK)] @@ -36,6 +37,8 @@ public class FlagsController( [ProducesResponseType(statusCode: StatusCodes.Status202Accepted)] public IActionResult CreateFlag([FromBody] CreateFlagRequest req) { + ValidationUtils.Validate(ValidateFlag(req.Name, req.Description, req.Image)); + var id = snowflakeGenerator.GenerateSnowflake(); queue.QueueInvocableWithPayload( @@ -45,14 +48,41 @@ public class FlagsController( } public record CreateFlagRequest(string Name, string Image, string? Description); + private record CreateFlagResponse(Snowflake Id, string Name, string? Description); + [HttpPatch("{id}")] + [Authorize("user.update")] + public async Task UpdateFlagAsync(Snowflake id, [FromBody] UpdateFlagRequest req) + { + ValidationUtils.Validate(ValidateFlag(req.Name, req.Description, null)); + + var flag = await db.PrideFlags.FirstOrDefaultAsync(f => f.Id == id && f.UserId == CurrentUser!.Id); + if (flag == null) throw new ApiError.NotFound("Unknown flag ID, or it's not your flag."); + + if (req.Name != null) flag.Name = req.Name; + + if (req.HasProperty(nameof(req.Description))) + flag.Description = req.Description; + + db.Update(flag); + await db.SaveChangesAsync(); + + return Ok(ToResponse(flag)); + } + + public class UpdateFlagRequest : PatchRequest + { + public string? Name { get; init; } + public string? Description { get; init; } + } + [HttpDelete("{id}")] [Authorize("user.update")] public async Task DeleteFlagAsync(Snowflake id) { await using var tx = await db.Database.BeginTransactionAsync(); - + var flag = await db.PrideFlags.FirstOrDefaultAsync(f => f.Id == id && f.UserId == CurrentUser!.Id); if (flag == null) throw new ApiError.NotFound("Unknown flag ID, or it's not your flag."); @@ -73,8 +103,9 @@ public class FlagsController( { _logger.Error(e, "Error deleting flag file {Hash}", hash); } - } else _logger.Debug("Flag file {Hash} is used by other flags, not deleting", hash); - + } + else _logger.Debug("Flag file {Hash} is used by other flags, not deleting", hash); + await tx.CommitAsync(); return NoContent(); @@ -88,4 +119,52 @@ public class FlagsController( string ImageUrl, string Name, string? Description); + + private static List<(string, ValidationError?)> ValidateFlag(string? name, string? description, string? imageData) + { + var errors = new List<(string, ValidationError?)>(); + + if (name != null) + { + switch (name.Length) + { + case < 1: + errors.Add(("name", ValidationError.LengthError("Name is too short", 1, 100, name.Length))); + break; + case > 100: + errors.Add(("name", ValidationError.LengthError("Name is too long", 1, 100, name.Length))); + break; + } + } + + if (description != null) + { + switch (description.Length) + { + case < 1: + errors.Add(("description", + ValidationError.LengthError("Description is too short", 1, 100, description.Length))); + break; + case > 500: + errors.Add(("description", + ValidationError.LengthError("Description is too long", 1, 100, description.Length))); + break; + } + } + + if (imageData != null) + { + switch (imageData.Length) + { + case 0: + errors.Add(("image", ValidationError.GenericValidationError("Image cannot be empty", null))); + break; + case > 1_500_000: + errors.Add(("image", ValidationError.GenericValidationError("Image is too large", null))); + break; + } + } + + return errors; + } } \ No newline at end of file