Refactor media attachments to use MediaFile entity

This commit introduces the MediaFile entity and updates the media attachments model, repositories, and related logic to reference MediaFile instead of storing file metadata directly in MediaAttachments. Controller, service, and SignalR request/response contracts are updated to support the new structure. Database configurations and validators are also adjusted accordingly. This refactor improves media management and normalization in the data model.
This commit is contained in:
Artemy
2025-07-06 21:09:12 +07:00
parent 42348c863f
commit 66819a015a
23 changed files with 166 additions and 93 deletions
@@ -0,0 +1,14 @@
using Govor.Core.Models;
namespace Govor.Application.Interfaces.Medias;
public interface IMediaService
{
public Task<MediaUploadResult> UploadMediaAsync(Media file);
public Task DeleteMediaAsync(Guid fileId);
public Task<MediaUploadResult> GetMediaAsync(string url);
}
public record Media(byte[] Data, string FileName, MediaType Type, string MineType, string EncryptedKey);
public record MediaUploadResult(Guid? MediaId, string Url);
@@ -17,8 +17,8 @@ public interface IMessageService
// Define specific result types for clarity, including original message for notifications if needed
public record SendMessageResult(bool IsSuccess, Exception? Exception, Guid MessageId)
: Result(IsSuccess, Exception, MessageId);
public record SendMessageResult(bool IsSuccess, Exception? Exception, Message Message)
: Result(IsSuccess, Exception, Message.Id);
public record EditMessageResult(bool IsSuccess, Exception? Exception, Message? OriginalMessage)
: Result(IsSuccess, Exception, OriginalMessage?.Id ?? Guid.Empty)
@@ -2,4 +2,4 @@ using Govor.Core.Models;
namespace Govor.Application.Interfaces.Messages.Parameters;
public record SendMedia(Guid MediaId, string EncryptedKey, MediaType Type, string MimeType);
public record SendMedia(Guid MediaId, string EncryptedKey);
+2 -1
View File
@@ -105,7 +105,8 @@ public class FriendsService : IFriendsService
try
{
var friendships = await _friendshipsRepository.FindByUserIdAsync(userId);
return friendships.Where(f => f.AddresseeId == userId && f.Status == FriendshipStatus.Pending).ToList();
return friendships.Where(f => f.AddresseeId == userId && f.Status == FriendshipStatus.Pending).ToList()
?? new List<Friendship>();
}
catch (NotFoundByKeyException<Guid> ex)
{
@@ -0,0 +1,29 @@
using Govor.Application.Interfaces;
using Govor.Application.Interfaces.Medias;
namespace Govor.Application.Services;
public class MediaService : IMediaService
{
private IStorageService _storageService;
public MediaService(IStorageService storageService)
{
_storageService = storageService;
}
public Task<MediaUploadResult> UploadMediaAsync(Media file)
{
throw new NotImplementedException();
}
public Task DeleteMediaAsync(Guid fileId)
{
throw new NotImplementedException();
}
public Task<MediaUploadResult> GetMediaAsync(string url)
{
throw new NotImplementedException();
}
}
+13 -16
View File
@@ -38,10 +38,10 @@ public class MessageService : IMessageService
// Validate recipient
if (sendParams.RecipientType == RecipientType.User)
{
if (!await _usersRepository.ExistsByIdAsync(sendParams.RecipientId)) // Changed to ExistsByIdAsync
if (!await _usersRepository.ExistsByIdAsync(sendParams.RecipientId))
{
_logger.LogWarning("Attempt to send message to non-existent user {RecipientId}", sendParams.RecipientId);
return new SendMessageResult(false, new KeyNotFoundException($"Recipient user {sendParams.RecipientId} not found."), Guid.Empty);
return new SendMessageResult(false, new KeyNotFoundException($"Recipient user {sendParams.RecipientId} not found."), default);
}
// Verify friendship for private messages
await _verifyFriendship.VerifyAsync(sendParams.FromUserId, sendParams.RecipientId);
@@ -51,7 +51,7 @@ public class MessageService : IMessageService
if (!_groupsRepository.Exists(sendParams.RecipientId))
{
_logger.LogWarning("Attempt to send message to non-existent group {GroupId}", sendParams.RecipientId);
return new SendMessageResult(false, new KeyNotFoundException($"Recipient group {sendParams.RecipientId} not found."), Guid.Empty);
return new SendMessageResult(false, new KeyNotFoundException($"Recipient group {sendParams.RecipientId} not found."), default);
}
// TODO: Optionally, verify if sender is a member of the group
// bool isMember = await _groupsRepository.IsUserMemberOfGroupAsync(sendParams.FromUserId, sendParams.RecipientId);
@@ -64,7 +64,7 @@ public class MessageService : IMessageService
else
{
_logger.LogError("Invalid recipient type specified: {RecipientType}", sendParams.RecipientType);
return new SendMessageResult(false, new ArgumentException("Invalid recipient type."), Guid.Empty);
return new SendMessageResult(false, new ArgumentException("Invalid recipient type."), default);
}
var messageId = Guid.NewGuid();
@@ -80,23 +80,20 @@ public class MessageService : IMessageService
ReplyToMessageId = sendParams.ReplyToMessageId,
MediaAttachments = sendParams.Media?.Select(m => new MediaAttachments
{
Id = m.MediaId, // Assuming SendMedia provides Id or it's generated here/by repo
Id = Guid.NewGuid(),
MessageId = messageId,
Type = m.Type,
MimeType = m.MimeType,
EncryptedKey = m.EncryptedKey,
// FilePath will be set by storage service or handled by repository if media ID is pre-generated
MediaFileId = m.MediaId
}).ToList() ?? new List<MediaAttachments>()
};
await _messagesRepository.AddAsync(message);
_logger.LogInformation("Message {MessageId} from {SenderId} to {RecipientId} ({RecipientType}) saved successfully.", messageId, sendParams.FromUserId, sendParams.RecipientId, sendParams.RecipientType);
return new SendMessageResult(true, null, messageId);
return new SendMessageResult(true, null, message);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error sending message from {SenderId} to {RecipientId} ({RecipientType})", sendParams.FromUserId, sendParams.RecipientId, sendParams.RecipientType);
return new SendMessageResult(false, ex, Guid.Empty);
return new SendMessageResult(false, ex, default);
}
}
@@ -133,12 +130,12 @@ public class MessageService : IMessageService
await _messagesRepository.UpdateAsync(message);
_logger.LogInformation("Message {MessageId} edited successfully by user {EditorId}", editParams.MessageId, editParams.EditorId);
return new EditMessageResult(true, null, originalMessageForNotification);
return new EditMessageResult(true, default, originalMessageForNotification);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error editing message {MessageId} by user {EditorId}", editParams.MessageId, editParams.EditorId);
return new EditMessageResult(false, ex, null);
return new EditMessageResult(false, ex, default);
}
}
@@ -159,7 +156,7 @@ public class MessageService : IMessageService
// }
// } else {
_logger.LogWarning("User {DeleterId} attempted to delete message {MessageId} not sent by them (sender was {SenderId})", deleteParams.DeleterId, deleteParams.MessageId, message.SenderId);
return new DeleteMessageResult(false, new UnauthorizedAccessException("User is not authorized to delete this message."), null);
return new DeleteMessageResult(false, new UnauthorizedAccessException("User is not authorized to delete this message."), default);
// }
}
@@ -174,12 +171,12 @@ public class MessageService : IMessageService
await _messagesRepository.RemoveAsync(deleteParams.MessageId);
_logger.LogInformation("Message {MessageId} deleted successfully by user {DeleterId}", deleteParams.MessageId, deleteParams.DeleterId);
return new DeleteMessageResult(true, null, originalMessageForNotification);
return new DeleteMessageResult(true, default, originalMessageForNotification);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error deleting message {MessageId} by user {DeleterId}", deleteParams.MessageId, deleteParams.DeleterId);
return new DeleteMessageResult(false, ex, null);
return new DeleteMessageResult(false, ex, default);
}
}
}