From 2b91723696e29533e1e8e7bad62da9619de6e5ad Mon Sep 17 00:00:00 2001 From: sam Date: Sun, 14 Jul 2024 21:41:16 +0200 Subject: [PATCH] feat(backend): add member delete endpoint --- .../Controllers/MembersController.cs | 23 +++++++++++++++++- Foxnouns.Backend/Jobs/AvatarUpdateJob.cs | 24 +++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/Foxnouns.Backend/Controllers/MembersController.cs b/Foxnouns.Backend/Controllers/MembersController.cs index 3aa06a9..7aea73c 100644 --- a/Foxnouns.Backend/Controllers/MembersController.cs +++ b/Foxnouns.Backend/Controllers/MembersController.cs @@ -6,6 +6,7 @@ using Foxnouns.Backend.Middleware; using Foxnouns.Backend.Services; using Foxnouns.Backend.Utils; using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; namespace Foxnouns.Backend.Controllers; @@ -14,7 +15,8 @@ public class MembersController( ILogger logger, DatabaseContext db, MemberRendererService memberRendererService, - ISnowflakeGenerator snowflakeGenerator) : ApiControllerBase + ISnowflakeGenerator snowflakeGenerator, + AvatarUpdateJob avatarUpdate) : ApiControllerBase { private readonly ILogger _logger = logger.ForContext(); @@ -74,5 +76,24 @@ public class MembersController( return Ok(memberRendererService.RenderMember(member, CurrentToken)); } + [HttpDelete("/api/v2/users/@me/members/{memberRef}")] + [Authorize("member.update")] + public async Task DeleteMemberAsync(string memberRef) + { + var member = await db.ResolveMemberAsync(CurrentUser!.Id, memberRef); + var deleteCount = await db.Members.Where(m => m.UserId == CurrentUser!.Id && m.Id == member.Id) + .ExecuteDeleteAsync(); + if (deleteCount == 0) + { + _logger.Warning("Successfully resolved member {Id} but could not delete them", member.Id); + return NoContent(); + } + + await db.SaveChangesAsync(); + + if (member.Avatar != null) await avatarUpdate.DeleteMemberAvatar(member.Id, member.Avatar); + return NoContent(); + } + public record CreateMemberRequest(string Name, string? DisplayName, string? Bio, string? Avatar, bool? Unlisted); } \ No newline at end of file diff --git a/Foxnouns.Backend/Jobs/AvatarUpdateJob.cs b/Foxnouns.Backend/Jobs/AvatarUpdateJob.cs index 139a384..bf3d35d 100644 --- a/Foxnouns.Backend/Jobs/AvatarUpdateJob.cs +++ b/Foxnouns.Backend/Jobs/AvatarUpdateJob.cs @@ -5,6 +5,7 @@ using Foxnouns.Backend.Utils; using Hangfire; using Minio; using Minio.DataModel.Args; +using Minio.Exceptions; using SixLabors.ImageSharp; using SixLabors.ImageSharp.Formats.Webp; using SixLabors.ImageSharp.Processing; @@ -163,6 +164,29 @@ public class AvatarUpdateJob(DatabaseContext db, IMinioClient minio, Config conf await db.SaveChangesAsync(); } + /// + /// Deletes a member's avatar. This should only be used when a member is in the process of being deleted, otherwise, + /// with a null avatar should be used instead. + /// + public async Task DeleteMemberAvatar(Snowflake id, string hash) => await DeleteAvatar(MemberAvatarPath(id, hash)); + /// + /// Deletes a user's avatar. This should only be used when a user is in the process of being deleted, otherwise, + /// with a null avatar should be used instead. + /// + public async Task DeleteUserAvatar(Snowflake id, string hash) => await DeleteAvatar(UserAvatarPath(id, hash)); + + private async Task DeleteAvatar(string path) + { + logger.Debug("Deleting avatar at path {Path}", path); + try + { + await minio.RemoveObjectAsync(new RemoveObjectArgs().WithBucket(config.Storage.Bucket).WithObject(path)); + } + catch (InvalidObjectNameException) + { + } + } + private async Task ConvertAvatar(string uri) { if (!uri.StartsWith("data:image/"))