💥 ♻️ Refactor cloud files' references, and loading system
This commit is contained in:
@ -92,7 +92,6 @@ public partial class ChatController(AppDatabase db, ChatService cs, ChatRoomServ
|
||||
.Include(m => m.Sender)
|
||||
.Include(m => m.Sender.Account)
|
||||
.Include(m => m.Sender.Account.Profile)
|
||||
.Include(m => m.Attachments)
|
||||
.Skip(offset)
|
||||
.Take(take)
|
||||
.ToListAsync();
|
||||
@ -169,6 +168,7 @@ public partial class ChatController(AppDatabase db, ChatService cs, ChatRoomServ
|
||||
.ToListAsync();
|
||||
message.Attachments = attachments
|
||||
.OrderBy(f => request.AttachmentsId.IndexOf(f.Id))
|
||||
.Select(f => f.ToReferenceObject())
|
||||
.ToList();
|
||||
}
|
||||
|
||||
@ -270,7 +270,6 @@ public partial class ChatController(AppDatabase db, ChatService cs, ChatRoomServ
|
||||
var message = await db.ChatMessages
|
||||
.Include(m => m.Sender)
|
||||
.Include(m => m.ChatRoom)
|
||||
.Include(m => m.Attachments)
|
||||
.FirstOrDefaultAsync(m => m.Id == messageId && m.ChatRoomId == roomId);
|
||||
|
||||
if (message == null) return NotFound();
|
||||
|
@ -12,7 +12,7 @@ public enum ChatRoomType
|
||||
DirectMessage
|
||||
}
|
||||
|
||||
public class ChatRoom : ModelBase
|
||||
public class ChatRoom : ModelBase, IIdentifiedResource
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
[MaxLength(1024)] public string? Name { get; set; }
|
||||
@ -21,10 +21,8 @@ public class ChatRoom : ModelBase
|
||||
public bool IsCommunity { get; set; }
|
||||
public bool IsPublic { get; set; }
|
||||
|
||||
[MaxLength(32)] public string? PictureId { get; set; }
|
||||
public CloudFile? Picture { get; set; }
|
||||
[MaxLength(32)] public string? BackgroundId { get; set; }
|
||||
public CloudFile? Background { get; set; }
|
||||
[Column(TypeName = "jsonb")] public CloudFileReferenceObject? Picture { get; set; }
|
||||
[Column(TypeName = "jsonb")] public CloudFileReferenceObject? Background { get; set; }
|
||||
|
||||
[JsonIgnore] public ICollection<ChatMember> Members { get; set; } = new List<ChatMember>();
|
||||
|
||||
@ -35,6 +33,8 @@ public class ChatRoom : ModelBase
|
||||
[JsonPropertyName("members")]
|
||||
public ICollection<ChatMemberTransmissionObject> DirectMembers { get; set; } =
|
||||
new List<ChatMemberTransmissionObject>();
|
||||
|
||||
public string ResourceIdentifier => $"chatroom/{Id}";
|
||||
}
|
||||
|
||||
public enum ChatMemberRole
|
||||
|
@ -17,6 +17,7 @@ namespace DysonNetwork.Sphere.Chat;
|
||||
public class ChatRoomController(
|
||||
AppDatabase db,
|
||||
FileService fs,
|
||||
FileReferenceService fileRefService,
|
||||
ChatRoomService crs,
|
||||
RealmService rs,
|
||||
ActionLogService als,
|
||||
@ -176,13 +177,13 @@ public class ChatRoomController(
|
||||
|
||||
if (request.PictureId is not null)
|
||||
{
|
||||
chatRoom.Picture = await db.Files.FindAsync(request.PictureId);
|
||||
chatRoom.Picture = (await db.Files.FindAsync(request.PictureId))?.ToReferenceObject();
|
||||
if (chatRoom.Picture is null) return BadRequest("Invalid picture id, unable to find the file on cloud.");
|
||||
}
|
||||
|
||||
if (request.BackgroundId is not null)
|
||||
{
|
||||
chatRoom.Background = await db.Files.FindAsync(request.BackgroundId);
|
||||
chatRoom.Background = (await db.Files.FindAsync(request.BackgroundId))?.ToReferenceObject();
|
||||
if (chatRoom.Background is null)
|
||||
return BadRequest("Invalid background id, unable to find the file on cloud.");
|
||||
}
|
||||
@ -190,10 +191,21 @@ public class ChatRoomController(
|
||||
db.ChatRooms.Add(chatRoom);
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
var chatRoomResourceId = $"chatroom:{chatRoom.Id}";
|
||||
|
||||
if (chatRoom.Picture is not null)
|
||||
await fs.MarkUsageAsync(chatRoom.Picture, 1);
|
||||
await fileRefService.CreateReferenceAsync(
|
||||
chatRoom.Picture.Id,
|
||||
"chat.room.picture",
|
||||
chatRoomResourceId
|
||||
);
|
||||
|
||||
if (chatRoom.Background is not null)
|
||||
await fs.MarkUsageAsync(chatRoom.Background, 1);
|
||||
await fileRefService.CreateReferenceAsync(
|
||||
chatRoom.Background.Id,
|
||||
"chat.room.background",
|
||||
chatRoomResourceId
|
||||
);
|
||||
|
||||
als.CreateActionLogFromRequest(
|
||||
ActionLogType.ChatroomCreate,
|
||||
@ -235,22 +247,50 @@ public class ChatRoomController(
|
||||
chatRoom.RealmId = member.RealmId;
|
||||
}
|
||||
|
||||
var chatRoomResourceId = $"chatroom:{chatRoom.Id}";
|
||||
|
||||
if (request.PictureId is not null)
|
||||
{
|
||||
var picture = await db.Files.FindAsync(request.PictureId);
|
||||
if (picture is null) return BadRequest("Invalid picture id, unable to find the file on cloud.");
|
||||
await fs.MarkUsageAsync(picture, 1);
|
||||
if (chatRoom.Picture is not null) await fs.MarkUsageAsync(chatRoom.Picture, -1);
|
||||
chatRoom.Picture = picture;
|
||||
|
||||
// Remove old references for pictures
|
||||
var oldPictureRefs = await fileRefService.GetResourceReferencesAsync(chatRoomResourceId, "chat.room.picture");
|
||||
foreach (var oldRef in oldPictureRefs)
|
||||
{
|
||||
await fileRefService.DeleteReferenceAsync(oldRef.Id);
|
||||
}
|
||||
|
||||
// Add a new reference
|
||||
await fileRefService.CreateReferenceAsync(
|
||||
picture.Id,
|
||||
"chat.room.picture",
|
||||
chatRoomResourceId
|
||||
);
|
||||
|
||||
chatRoom.Picture = picture.ToReferenceObject();
|
||||
}
|
||||
|
||||
if (request.BackgroundId is not null)
|
||||
{
|
||||
var background = await db.Files.FindAsync(request.BackgroundId);
|
||||
if (background is null) return BadRequest("Invalid background id, unable to find the file on cloud.");
|
||||
await fs.MarkUsageAsync(background, 1);
|
||||
if (chatRoom.Background is not null) await fs.MarkUsageAsync(chatRoom.Background, -1);
|
||||
chatRoom.Background = background;
|
||||
|
||||
// Remove old references for backgrounds
|
||||
var oldBackgroundRefs = await fileRefService.GetResourceReferencesAsync(chatRoomResourceId, "chat.room.background");
|
||||
foreach (var oldRef in oldBackgroundRefs)
|
||||
{
|
||||
await fileRefService.DeleteReferenceAsync(oldRef.Id);
|
||||
}
|
||||
|
||||
// Add a new reference
|
||||
await fileRefService.CreateReferenceAsync(
|
||||
background.Id,
|
||||
"chat.room.background",
|
||||
chatRoomResourceId
|
||||
);
|
||||
|
||||
chatRoom.Background = background.ToReferenceObject();
|
||||
}
|
||||
|
||||
if (request.Name is not null)
|
||||
@ -293,14 +333,14 @@ public class ChatRoomController(
|
||||
else if (!await crs.IsMemberWithRole(chatRoom.Id, currentUser.Id, ChatMemberRole.Owner))
|
||||
return StatusCode(403, "You need at least be the owner to delete the chat.");
|
||||
|
||||
var chatRoomResourceId = $"chatroom:{chatRoom.Id}";
|
||||
|
||||
// Delete all file references for this chat room
|
||||
await fileRefService.DeleteResourceReferencesAsync(chatRoomResourceId);
|
||||
|
||||
db.ChatRooms.Remove(chatRoom);
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
if (chatRoom.Picture is not null)
|
||||
await fs.MarkUsageAsync(chatRoom.Picture, -1);
|
||||
if (chatRoom.Background is not null)
|
||||
await fs.MarkUsageAsync(chatRoom.Background, -1);
|
||||
|
||||
als.CreateActionLogFromRequest(
|
||||
ActionLogType.ChatroomDelete,
|
||||
new Dictionary<string, object> { { "chatroom_id", chatRoom.Id } }, Request
|
||||
|
@ -10,6 +10,7 @@ namespace DysonNetwork.Sphere.Chat;
|
||||
public class ChatService(
|
||||
AppDatabase db,
|
||||
FileService fs,
|
||||
FileReferenceService fileRefService,
|
||||
IServiceScopeFactory scopeFactory,
|
||||
IRealtimeService realtime,
|
||||
ILogger<ChatService> logger
|
||||
@ -30,9 +31,16 @@ public class ChatService(
|
||||
var files = message.Attachments.Distinct().ToList();
|
||||
if (files.Count != 0)
|
||||
{
|
||||
await fs.MarkUsageRangeAsync(files, 1);
|
||||
await fs.SetExpiresRangeAsync(files, Duration.FromDays(30));
|
||||
await fs.SetUsageRangeAsync(files, ChatFileUsageIdentifier);
|
||||
var messageResourceId = $"message:{message.Id}";
|
||||
foreach (var file in files)
|
||||
{
|
||||
await fileRefService.CreateReferenceAsync(
|
||||
file.Id,
|
||||
ChatFileUsageIdentifier,
|
||||
messageResourceId,
|
||||
duration: Duration.FromDays(30)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Then start the delivery process
|
||||
@ -64,7 +72,7 @@ public class ChatService(
|
||||
{
|
||||
message.Sender = sender;
|
||||
message.ChatRoom = room;
|
||||
|
||||
|
||||
using var scope = scopeFactory.CreateScope();
|
||||
var scopedWs = scope.ServiceProvider.GetRequiredService<WebSocketService>();
|
||||
var scopedNty = scope.ServiceProvider.GetRequiredService<NotificationService>();
|
||||
@ -87,8 +95,8 @@ public class ChatService(
|
||||
.Where(a => a.MimeType != null && a.MimeType.StartsWith("image"))
|
||||
.Select(a => a.Id).ToList()
|
||||
};
|
||||
if (sender.Account.Profile is not { PictureId: null })
|
||||
metaDict["pfp"] = sender.Account.Profile.PictureId;
|
||||
if (sender.Account.Profile is not { Picture: null })
|
||||
metaDict["pfp"] = sender.Account.Profile.Picture.Id;
|
||||
if (!string.IsNullOrEmpty(room.Name))
|
||||
metaDict["room_name"] = room.Name;
|
||||
|
||||
@ -346,9 +354,28 @@ public class ChatService(
|
||||
|
||||
if (attachmentsId is not null)
|
||||
{
|
||||
message.Attachments = (await fs.DiffAndMarkFilesAsync(attachmentsId, message.Attachments)).current;
|
||||
await fs.DiffAndSetExpiresAsync(attachmentsId, Duration.FromDays(30), message.Attachments);
|
||||
await fs.DiffAndSetUsageAsync(attachmentsId, ChatFileUsageIdentifier, message.Attachments);
|
||||
var messageResourceId = $"message:{message.Id}";
|
||||
|
||||
// Delete existing references for this message
|
||||
await fileRefService.DeleteResourceReferencesAsync(messageResourceId);
|
||||
|
||||
// Create new references for each attachment
|
||||
foreach (var fileId in attachmentsId)
|
||||
{
|
||||
await fileRefService.CreateReferenceAsync(
|
||||
fileId,
|
||||
ChatFileUsageIdentifier,
|
||||
messageResourceId,
|
||||
duration: Duration.FromDays(30)
|
||||
);
|
||||
}
|
||||
|
||||
// Update message attachments by getting files from database
|
||||
var files = await db.Files
|
||||
.Where(f => attachmentsId.Contains(f.Id))
|
||||
.ToListAsync();
|
||||
|
||||
message.Attachments = files.Select(x => x.ToReferenceObject()).ToList();
|
||||
}
|
||||
|
||||
message.EditedAt = SystemClock.Instance.GetCurrentInstant();
|
||||
@ -371,9 +398,9 @@ public class ChatService(
|
||||
/// <param name="message">The message to delete</param>
|
||||
public async Task DeleteMessageAsync(Message message)
|
||||
{
|
||||
var files = message.Attachments.Distinct().ToList();
|
||||
if (files.Count != 0)
|
||||
await fs.MarkUsageRangeAsync(files, -1);
|
||||
// Remove all file references for this message
|
||||
var messageResourceId = $"message:{message.Id}";
|
||||
await fileRefService.DeleteResourceReferencesAsync(messageResourceId);
|
||||
|
||||
db.ChatMessages.Remove(message);
|
||||
await db.SaveChangesAsync();
|
||||
|
@ -7,7 +7,7 @@ using NodaTime;
|
||||
|
||||
namespace DysonNetwork.Sphere.Chat;
|
||||
|
||||
public class Message : ModelBase
|
||||
public class Message : ModelBase, IIdentifiedResource
|
||||
{
|
||||
public Guid Id { get; set; } = Guid.NewGuid();
|
||||
[MaxLength(1024)] public string Type { get; set; } = null!;
|
||||
@ -17,7 +17,7 @@ public class Message : ModelBase
|
||||
[MaxLength(36)] public string Nonce { get; set; } = null!;
|
||||
public Instant? EditedAt { get; set; }
|
||||
|
||||
public ICollection<CloudFile> Attachments { get; set; } = new List<CloudFile>();
|
||||
[Column(TypeName = "jsonb")] public List<CloudFileReferenceObject> Attachments { get; set; } = [];
|
||||
public ICollection<MessageReaction> Reactions { get; set; } = new List<MessageReaction>();
|
||||
|
||||
public Guid? RepliedMessageId { get; set; }
|
||||
@ -29,32 +29,8 @@ public class Message : ModelBase
|
||||
public ChatMember Sender { get; set; } = null!;
|
||||
public Guid ChatRoomId { get; set; }
|
||||
[JsonIgnore] public ChatRoom ChatRoom { get; set; } = null!;
|
||||
|
||||
public Message Clone()
|
||||
{
|
||||
return new Message
|
||||
{
|
||||
Id = Id,
|
||||
Content = Content,
|
||||
Meta = Meta?.ToDictionary(entry => entry.Key, entry => entry.Value),
|
||||
MembersMentioned = MembersMentioned?.ToList(),
|
||||
Nonce = Nonce,
|
||||
EditedAt = EditedAt,
|
||||
Attachments = new List<CloudFile>(Attachments),
|
||||
Reactions = new List<MessageReaction>(Reactions),
|
||||
RepliedMessageId = RepliedMessageId,
|
||||
RepliedMessage = RepliedMessage?.Clone() as Message,
|
||||
ForwardedMessageId = ForwardedMessageId,
|
||||
ForwardedMessage = ForwardedMessage?.Clone() as Message,
|
||||
SenderId = SenderId,
|
||||
Sender = Sender,
|
||||
ChatRoomId = ChatRoomId,
|
||||
ChatRoom = ChatRoom,
|
||||
CreatedAt = CreatedAt,
|
||||
UpdatedAt = UpdatedAt,
|
||||
DeletedAt = DeletedAt
|
||||
};
|
||||
}
|
||||
|
||||
public string ResourceIdentifier => $"message/{Id}";
|
||||
}
|
||||
|
||||
public enum MessageReactionAttitude
|
||||
|
Reference in New Issue
Block a user