diff --git a/Foxnouns.Backend/Controllers/FlagsController.cs b/Foxnouns.Backend/Controllers/FlagsController.cs index 7bf20e5..31f3400 100644 --- a/Foxnouns.Backend/Controllers/FlagsController.cs +++ b/Foxnouns.Backend/Controllers/FlagsController.cs @@ -1,6 +1,7 @@ using Coravel.Queuing.Interfaces; using Foxnouns.Backend.Database; using Foxnouns.Backend.Database.Models; +using Foxnouns.Backend.Extensions; using Foxnouns.Backend.Jobs; using Foxnouns.Backend.Middleware; using Foxnouns.Backend.Services; @@ -11,11 +12,15 @@ namespace Foxnouns.Backend.Controllers; [Route("/api/v2/users/@me/flags")] public class FlagsController( + ILogger logger, DatabaseContext db, UserRendererService userRenderer, + ObjectStorageService objectStorageService, ISnowflakeGenerator snowflakeGenerator, IQueue queue) : ApiControllerBase { + private readonly ILogger _logger = logger.ForContext(); + [HttpGet] [Authorize("identify")] [ProducesResponseType>(statusCode: StatusCodes.Status200OK)] @@ -40,8 +45,40 @@ public class FlagsController( } public record CreateFlagRequest(string Name, string Image, string? Description); + private record CreateFlagResponse(Snowflake Id, string Name, string? Description); - public record CreateFlagResponse(Snowflake Id, string Name, string? Description); + [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."); + + var hash = flag.Hash; + + db.PrideFlags.Remove(flag); + await db.SaveChangesAsync(); + + var flagCount = await db.PrideFlags.CountAsync(f => f.Hash == flag.Hash); + if (flagCount == 0) + { + try + { + _logger.Information("Deleting flag file {Hash} as it is no longer used by any flags", hash); + await objectStorageService.DeleteFlagAsync(hash); + } + catch (Exception e) + { + _logger.Error(e, "Error deleting flag file {Hash}", hash); + } + } else _logger.Debug("Flag file {Hash} is used by other flags, not deleting", hash); + + await tx.CommitAsync(); + + return NoContent(); + } private PrideFlagResponse ToResponse(PrideFlag flag) => new(flag.Id, userRenderer.ImageUrlFor(flag), flag.Name, flag.Description); diff --git a/Foxnouns.Backend/Extensions/AvatarObjectExtensions.cs b/Foxnouns.Backend/Extensions/AvatarObjectExtensions.cs index 063f835..6aa1626 100644 --- a/Foxnouns.Backend/Extensions/AvatarObjectExtensions.cs +++ b/Foxnouns.Backend/Extensions/AvatarObjectExtensions.cs @@ -24,6 +24,10 @@ public static class AvatarObjectExtensions CancellationToken ct = default) => await objectStorageService.RemoveObjectAsync(UserAvatarUpdateInvocable.Path(id, hash), ct); + public static async Task DeleteFlagAsync(this ObjectStorageService objectStorageService, string hash, + CancellationToken ct = default) => + await objectStorageService.RemoveObjectAsync(CreateFlagInvocable.Path(hash), ct); + public static async Task<(string Hash, Stream Image)> ConvertBase64UriToImage(this string uri, int size, bool crop) { if (!uri.StartsWith("data:image/")) diff --git a/Foxnouns.Backend/Jobs/CreateFlagInvocable.cs b/Foxnouns.Backend/Jobs/CreateFlagInvocable.cs index 1acf054..0d99244 100644 --- a/Foxnouns.Backend/Jobs/CreateFlagInvocable.cs +++ b/Foxnouns.Backend/Jobs/CreateFlagInvocable.cs @@ -1,4 +1,3 @@ -using System.Security.Cryptography; using Coravel.Invocable; using Foxnouns.Backend.Database; using Foxnouns.Backend.Database.Models; @@ -45,5 +44,5 @@ public class CreateFlagInvocable(DatabaseContext db, ObjectStorageService object throw new NotImplementedException(); } - private static string Path(string hash) => $"flags/{hash}.webp"; + public static string Path(string hash) => $"flags/{hash}.webp"; } \ No newline at end of file