109 lines
3.8 KiB
C#
109 lines
3.8 KiB
C#
using System;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Knot.Shared.Kernel;
|
|
using Knot.Contracts.Conversations.Domain;
|
|
using Knot.Contracts.Conversations.Application.Abstractions;
|
|
using MediatR;
|
|
using System.Linq;
|
|
using Knot.Contracts.Messaging.Application.Abstractions;
|
|
using Knot.Shared.Kernel.Storage;
|
|
|
|
namespace Knot.Modules.Conversations.Application.Chats.LeaveOrDelete;
|
|
|
|
public record LeaveOrDeleteChatCommand(Guid ChatId, Guid UserId) : ICommand<SuccessResponse>;
|
|
|
|
internal sealed class LeaveOrDeleteChatCommandHandler : ICommandHandler<LeaveOrDeleteChatCommand, SuccessResponse>
|
|
{
|
|
private readonly IChatRepository _chatRepository;
|
|
private readonly IMessageRepository _messageRepository;
|
|
private readonly IFileStorageService _fileStorage;
|
|
private readonly IChatsUnitOfWork _uow;
|
|
|
|
public LeaveOrDeleteChatCommandHandler(
|
|
IChatRepository chatRepository,
|
|
IMessageRepository messageRepository,
|
|
IFileStorageService fileStorage,
|
|
IChatsUnitOfWork uow)
|
|
{
|
|
_chatRepository = chatRepository;
|
|
_messageRepository = messageRepository;
|
|
_fileStorage = fileStorage;
|
|
_uow = uow;
|
|
}
|
|
|
|
public async Task<Result<SuccessResponse>> Handle(LeaveOrDeleteChatCommand request, CancellationToken cancellationToken)
|
|
{
|
|
var chat = await _chatRepository.GetByIdAsync(request.ChatId, cancellationToken);
|
|
if (chat == null)
|
|
{
|
|
return Result.Success(new SuccessResponse(true));
|
|
}
|
|
|
|
if (!chat.Members.Any(m => m.UserId == request.UserId))
|
|
{
|
|
return Result.Failure<SuccessResponse>(ChatErrors.Unauthorized);
|
|
}
|
|
|
|
// If it's a private chat or the last member leaving a group, delete everything
|
|
bool shouldDeleteEverything = chat.Type != ChatType.Group || chat.Members.Count <= 1;
|
|
|
|
if (chat.Type == ChatType.Group && !shouldDeleteEverything)
|
|
{
|
|
chat.RemoveMember(request.UserId);
|
|
_chatRepository.Update(chat);
|
|
}
|
|
else
|
|
{
|
|
// DELETE ALL MESSAGES AND FILES FIRST
|
|
await DeleteChatMediaAndMessagesAsync(chat.Id, cancellationToken);
|
|
_chatRepository.Remove(chat);
|
|
}
|
|
|
|
await _uow.SaveChangesAsync(cancellationToken);
|
|
|
|
return Result.Success(new SuccessResponse(true));
|
|
}
|
|
|
|
private async Task DeleteChatMediaAndMessagesAsync(Guid chatId, CancellationToken ct)
|
|
{
|
|
try
|
|
{
|
|
// Get all messages directly from Mongo (not paged)
|
|
var messages = await _messageRepository.GetChatMessagesAsync(chatId, int.MaxValue, 0, ct);
|
|
foreach (var msg in messages)
|
|
{
|
|
if (msg is Knot.Contracts.Messaging.Domain.MediaMessage mediaMsg)
|
|
{
|
|
foreach (var media in mediaMsg.Media)
|
|
{
|
|
if (!string.IsNullOrEmpty(media.Url))
|
|
{
|
|
var fileId = ExtractFileId(media.Url);
|
|
if (!string.IsNullOrEmpty(fileId))
|
|
{
|
|
await _fileStorage.DeleteFileAsync(fileId);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
await _messageRepository.DeleteChatMessagesAsync(chatId, ct);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// Log if possible, but don't fail chat deletion
|
|
Console.WriteLine($"[Cleanup] Error deleting chat media: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
private string? ExtractFileId(string url)
|
|
{
|
|
var lastSlash = url.LastIndexOf('/');
|
|
if (lastSlash == -1) return null;
|
|
var id = url[(lastSlash + 1)..];
|
|
if (id.Contains('?')) id = id[..id.IndexOf('?')];
|
|
return id;
|
|
}
|
|
}
|