using System; using System.Linq; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using MediatR; using Knot.Shared.Kernel; using Knot.Shared.Kernel.Storage; using Knot.Modules.Identity.Infrastructure.Persistence; using Knot.Modules.Chats.Infrastructure.Persistence; using Knot.Modules.Chats.Domain; using MongoDB.Driver; using Microsoft.EntityFrameworkCore; using Host.Models; namespace Host.Application.Admin.Queries; public record CleanDryRunQuery(IFileStorageService FileStorage, IdentityDbContext IdentityDb) : IQuery; internal sealed class CleanDryRunQueryHandler : IQueryHandler { private readonly ChatsDbContext _chatsDbContext; private readonly IMongoCollection _messages; public CleanDryRunQueryHandler(ChatsDbContext chatsDbContext, IMongoDatabase mongoDb) { _chatsDbContext = chatsDbContext; _messages = mongoDb.GetCollection("Messages"); } public async Task> Handle(CleanDryRunQuery request, CancellationToken cancellationToken) { var allChats = await _chatsDbContext.Chats.AsNoTracking().ToListAsync(cancellationToken); var activeChatIds = allChats.Select(c => c.Id).ToHashSet(); var allMessages = await _messages.Find(_ => true).ToListAsync(cancellationToken); var orphanedMessages = allMessages .Where(m => !activeChatIds.Contains(m.ChatId)) .ToList(); var keptMessages = allMessages .Where(m => activeChatIds.Contains(m.ChatId)) .ToList(); var orphanedMessagesCount = orphanedMessages.Count; var allMinioFiles = (await request.FileStorage.ListFilesAsync()).ToList(); var allUsers = await request.IdentityDb.Users.AsNoTracking().ToListAsync(cancellationToken); var validUrls = new HashSet(); var activeMessageUrls = keptMessages.OfType() .Where(m => m.Media != null) .SelectMany(m => m.Media) .Select(me => me.Url) .Where(u => !string.IsNullOrEmpty(u)); var activeChatUrls = allChats .Where(c => !string.IsNullOrEmpty(c.Avatar)) .Select(c => c.Avatar!); var activeUserUrls = allUsers .Where(u => !string.IsNullOrEmpty(u.Avatar)) .Select(u => u.Avatar!); foreach (var u in activeMessageUrls) { validUrls.Add(u!); } foreach (var u in activeChatUrls) { validUrls.Add(u); } foreach (var u in activeUserUrls) { validUrls.Add(u); } var validFileIds = validUrls .Where(u => u.Contains("/api/files/")) .Select(u => u.Split('/').Last()) .ToHashSet(); long safeBytes = 0; foreach (var file in allMinioFiles) { if (!validFileIds.Contains(file.FileId)) { safeBytes += file.Size; } } return Result.Success(new CleanupDryRunResultDto { OrphanedMessagesCount = orphanedMessagesCount, OrphanedMediaBytes = safeBytes }); } }