Files
QMAX/server/QMax.Api/Data/QMaxDatabaseCleanup.cs
T
2026-07-05 08:58:38 +03:00

155 lines
6.3 KiB
C#

using Microsoft.EntityFrameworkCore;
using QMax.Api.Data.Entities;
namespace QMax.Api.Data;
public static class QMaxDatabaseCleanup
{
public static async Task RemoveLegacyWebFileAttachmentEchoesAsync(QMaxDbContext db)
{
var connection = db.Database.GetDbConnection();
if (connection.State != System.Data.ConnectionState.Open)
{
await connection.OpenAsync();
}
await db.Database.ExecuteSqlRawAsync("DROP TABLE IF EXISTS temp.LegacyWebFileMessageIds;");
await db.Database.ExecuteSqlRawAsync("""
CREATE TEMP TABLE LegacyWebFileMessageIds (
Id TEXT NOT NULL PRIMARY KEY
);
""");
await db.Database.ExecuteSqlRawAsync("""
INSERT OR IGNORE INTO LegacyWebFileMessageIds (Id)
SELECT DISTINCT legacy.Id
FROM Messages AS legacy
INNER JOIN MessageAttachments AS legacyAttachment ON legacyAttachment.MessageId = legacy.Id
WHERE legacy.Direction = 'Outgoing'
AND legacy.ExternalId LIKE 'web-file-%'
AND legacyAttachment.Sha256 IS NOT NULL
AND legacyAttachment.Sha256 <> ''
AND EXISTS (
SELECT 1
FROM Messages AS confirmed
INNER JOIN MessageAttachments AS confirmedAttachment ON confirmedAttachment.MessageId = confirmed.Id
WHERE confirmed.ChatId = legacy.ChatId
AND confirmed.Id <> legacy.Id
AND confirmed.Direction = 'Outgoing'
AND confirmed.ExternalId IS NOT NULL
AND confirmed.ExternalId NOT LIKE 'web-file-%'
AND confirmedAttachment.Sha256 = legacyAttachment.Sha256
);
""");
await db.Database.ExecuteSqlRawAsync("""
DELETE FROM MessageAttachments
WHERE MessageId IN (SELECT Id FROM LegacyWebFileMessageIds);
""");
await db.Database.ExecuteSqlRawAsync("""
DELETE FROM Messages
WHERE Id IN (SELECT Id FROM LegacyWebFileMessageIds);
""");
await db.Database.ExecuteSqlRawAsync("DROP TABLE IF EXISTS temp.LegacyWebFileMessageIds;");
}
public static async Task MergeOutgoingRemoteAttachmentEchoesAsync(QMaxDbContext db)
{
var connection = db.Database.GetDbConnection();
if (connection.State != System.Data.ConnectionState.Open)
{
await connection.OpenAsync();
}
await db.Database.ExecuteSqlRawAsync("DROP TABLE IF EXISTS temp.OutgoingAttachmentEchoMerges;");
await db.Database.ExecuteSqlRawAsync("""
CREATE TEMP TABLE OutgoingAttachmentEchoMerges (
LocalId TEXT NOT NULL PRIMARY KEY,
RemoteId TEXT NOT NULL,
ExternalId TEXT NULL,
RemoteUrl TEXT NULL
);
""");
await db.Database.ExecuteSqlRawAsync("""
INSERT OR IGNORE INTO OutgoingAttachmentEchoMerges (LocalId, RemoteId, ExternalId, RemoteUrl)
SELECT localAttachment.Id,
remoteAttachment.Id,
remoteAttachment.ExternalId,
remoteAttachment.RemoteUrl
FROM Messages AS message
INNER JOIN MessageAttachments AS localAttachment ON localAttachment.MessageId = message.Id
INNER JOIN MessageAttachments AS remoteAttachment ON remoteAttachment.MessageId = message.Id
WHERE message.Direction = 'Outgoing'
AND localAttachment.Id <> remoteAttachment.Id
AND localAttachment.SortOrder = remoteAttachment.SortOrder
AND localAttachment.Kind = remoteAttachment.Kind
AND (localAttachment.ExternalId IS NULL OR trim(localAttachment.ExternalId) = '')
AND (localAttachment.RemoteUrl IS NULL OR trim(localAttachment.RemoteUrl) = '')
AND localAttachment.StorageFileName NOT LIKE 'remote-%'
AND (
(remoteAttachment.ExternalId IS NOT NULL AND trim(remoteAttachment.ExternalId) <> '') OR
(remoteAttachment.RemoteUrl IS NOT NULL AND trim(remoteAttachment.RemoteUrl) <> '') OR
remoteAttachment.StorageFileName LIKE 'remote-%'
);
""");
await db.Database.ExecuteSqlRawAsync("""
DELETE FROM MessageAttachments
WHERE Id IN (SELECT RemoteId FROM OutgoingAttachmentEchoMerges);
""");
await db.Database.ExecuteSqlRawAsync("""
UPDATE MessageAttachments
SET ExternalId = COALESCE(NULLIF(ExternalId, ''), (
SELECT ExternalId
FROM OutgoingAttachmentEchoMerges
WHERE LocalId = MessageAttachments.Id
)),
RemoteUrl = COALESCE(NULLIF(RemoteUrl, ''), (
SELECT RemoteUrl
FROM OutgoingAttachmentEchoMerges
WHERE LocalId = MessageAttachments.Id
))
WHERE Id IN (SELECT LocalId FROM OutgoingAttachmentEchoMerges);
""");
await db.Database.ExecuteSqlRawAsync("DROP TABLE IF EXISTS temp.OutgoingAttachmentEchoMerges;");
}
public static async Task ClearUnreadCountsForLatestOutgoingChatsAsync(QMaxDbContext db)
{
var unreadChats = await db.Chats
.Where(chat => chat.UnreadCount > 0)
.ToListAsync();
if (unreadChats.Count == 0)
{
return;
}
var changed = false;
foreach (var chat in unreadChats)
{
var chatMessages = await db.Messages
.Where(message => message.ChatId == chat.Id && message.DeletedAt == null)
.Select(message => new { message.Direction, message.SentAt, message.Id })
.ToListAsync();
var latestMessage = chatMessages
.OrderByDescending(message => message.SentAt)
.ThenByDescending(message => message.Id)
.FirstOrDefault();
if (latestMessage?.Direction != MessageDirection.Outgoing)
{
continue;
}
chat.UnreadCount = 0;
chat.UpdatedAt = DateTimeOffset.UtcNow;
changed = true;
}
if (changed)
{
await db.SaveChangesAsync();
}
}
}