A persona's avatar and header can be removed

Mastodon's DELETE /api/v1/profile/avatar and /api/v1/profile/header: the picture goes to the trash (its file stops
being served at once), the profile shows the default again, followers are sent the updated profile, and the answer is
the account with its source. The profile Update is the one a persona that never had a picture sends, so nothing new
leaves PrivaPub. MediaLifecycleTests: the avatar, then the header, removed and no longer served.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
This commit is contained in:
thepraandClaude Opus 5.5 committed 2026-10-07 10:55:12 +02:00
1 parent 3ca29603ed
commit 8fecf7de2d
2 files changed
+45

No files matched your search

@@ -123,6 +123,28 @@ namespace PrivaPub.Api.Mastodon.Controllers
return Json(await _mapper.Local(actor, withSource: true, token));
}
// Mastodon's: the picture goes (to the trash), the profile shows the default again, and followers are told
[HttpDelete("/api/v1/profile/avatar"), Scope("write:accounts")]
public Task<IActionResult> DeleteAvatar([FromServices] IMediaService media, CancellationToken token) => DeletePicture(media, "avatar", token);
[HttpDelete("/api/v1/profile/header"), Scope("write:accounts")]
public Task<IActionResult> DeleteHeader([FromServices] IMediaService media, CancellationToken token) => DeletePicture(media, "header", token);
async Task<IActionResult> DeletePicture(IMediaService media, string kind, CancellationToken token)
{
var avatar = await _dbEntities.Avatars.MatchID(Me.Id).ExecuteFirstAsync(token);
if (kind == "avatar")
avatar.PictureURL = default;
else
avatar.ThumbnailURL = default;
avatar.UpdatedAt = DateTime.UtcNow;
await DB.Default.SaveAsync(avatar, token);
await media.Trash(m => m.ProfileOfAvatarId == avatar.ID && m.Kind == kind, "removed", token);
var actor = _localActors.FromAvatar(avatar);
await _outbox.PublishProfile(actor, token);
return Json(await _mapper.Local(actor, withSource: true, token));
}
[HttpGet("/api/v1/accounts/lookup"), Scope("read:accounts", requiresUser: false), Microsoft.AspNetCore.Authorization.AllowAnonymous]
public async Task<IActionResult> Lookup(CancellationToken token)
{