Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
170 lines
4.7 KiB
TypeScript
170 lines
4.7 KiB
TypeScript
import { api } from '@rocket.chat/core-services';
|
|
import type { IRoom } from '@rocket.chat/core-typings';
|
|
import { Messages, Rooms, Subscriptions, ReadReceipts, ReadReceiptsArchive } from '@rocket.chat/models';
|
|
|
|
import { deleteRoom } from './deleteRoom';
|
|
import { NOTIFICATION_ATTACHMENT_COLOR } from '../../../lib/constants';
|
|
import { i18n } from '../i18n';
|
|
import { FileUpload } from '../media/file-upload';
|
|
import { notifyOnRoomChangedById, notifyOnSubscriptionChangedById } from '../notifyListener';
|
|
|
|
const FILE_CLEANUP_BATCH_SIZE = 1000;
|
|
|
|
export async function cleanRoomHistory({
|
|
rid = '',
|
|
latest = new Date(),
|
|
oldest = new Date('0001-01-01T00:00:00Z'),
|
|
inclusive = true,
|
|
limit = 0,
|
|
excludePinned = true,
|
|
ignoreDiscussion = true,
|
|
filesOnly = false,
|
|
fromUsers = [],
|
|
ignoreThreads = true,
|
|
}: {
|
|
rid?: IRoom['_id'];
|
|
latest?: Date;
|
|
oldest?: Date;
|
|
inclusive?: boolean;
|
|
limit?: number;
|
|
excludePinned?: boolean;
|
|
ignoreDiscussion?: boolean;
|
|
filesOnly?: boolean;
|
|
fromUsers?: string[];
|
|
ignoreThreads?: boolean;
|
|
}): Promise<number> {
|
|
const gt = inclusive ? '$gte' : '$gt';
|
|
const lt = inclusive ? '$lte' : '$lt';
|
|
|
|
const ts = { [gt]: oldest, [lt]: latest };
|
|
|
|
const text = `_${i18n.t('File_removed_by_prune')}_`;
|
|
|
|
let fileCount = 0;
|
|
|
|
const cursor = Messages.findFilesByRoomIdPinnedTimestampAndUsers(rid, excludePinned, ignoreDiscussion, ts, fromUsers, ignoreThreads, {
|
|
projection: { pinned: 1, files: 1 },
|
|
limit,
|
|
});
|
|
|
|
const targetMessageIdsForAttachmentRemoval = new Set<string>();
|
|
// Since we remove every file from the messages, we don't need to specify which fileId has been removed.
|
|
const pruneMessageAttachment = { type: 'removed-file', color: NOTIFICATION_ATTACHMENT_COLOR, text };
|
|
|
|
async function performFileAttachmentCleanupBatch() {
|
|
if (targetMessageIdsForAttachmentRemoval.size === 0) return;
|
|
|
|
const ids = [...targetMessageIdsForAttachmentRemoval];
|
|
await Messages.removeFileAttachmentsByMessageIds(ids, pruneMessageAttachment);
|
|
await Messages.clearFilesByMessageIds(ids);
|
|
void api.broadcast('notify.deleteMessageBulk', rid, {
|
|
rid,
|
|
excludePinned,
|
|
ignoreDiscussion,
|
|
ts,
|
|
users: fromUsers,
|
|
ids,
|
|
filesOnly: true,
|
|
replaceFileAttachmentsWith: pruneMessageAttachment,
|
|
});
|
|
targetMessageIdsForAttachmentRemoval.clear();
|
|
}
|
|
|
|
for await (const document of cursor) {
|
|
const uploadsStore = FileUpload.getStore('Uploads');
|
|
|
|
document.files && (await Promise.all(document.files.map((file) => uploadsStore.deleteById(file._id))));
|
|
|
|
fileCount++;
|
|
if (filesOnly) {
|
|
targetMessageIdsForAttachmentRemoval.add(document._id);
|
|
}
|
|
|
|
if (targetMessageIdsForAttachmentRemoval.size >= FILE_CLEANUP_BATCH_SIZE) {
|
|
await performFileAttachmentCleanupBatch();
|
|
}
|
|
}
|
|
|
|
if (targetMessageIdsForAttachmentRemoval.size > 0) {
|
|
await performFileAttachmentCleanupBatch();
|
|
}
|
|
|
|
if (filesOnly) {
|
|
return fileCount;
|
|
}
|
|
|
|
if (!ignoreDiscussion) {
|
|
const discussionsCursor = Messages.findDiscussionByRoomIdPinnedTimestampAndUsers(rid, excludePinned, ts, fromUsers, {
|
|
projection: { drid: 1 },
|
|
...(limit && { limit }),
|
|
});
|
|
|
|
for await (const { drid } of discussionsCursor) {
|
|
if (!drid) {
|
|
continue;
|
|
}
|
|
await deleteRoom(drid);
|
|
}
|
|
}
|
|
|
|
if (!ignoreThreads) {
|
|
const threads = new Set<string>();
|
|
|
|
await Messages.findThreadsByRoomIdPinnedTimestampAndUsers(
|
|
{ rid, pinned: excludePinned, ignoreDiscussion, ts, users: fromUsers },
|
|
{ projection: { _id: 1 } },
|
|
).forEach(({ _id }) => {
|
|
threads.add(_id);
|
|
});
|
|
|
|
if (threads.size > 0) {
|
|
const subscriptionIds: string[] = (
|
|
await Subscriptions.findUnreadThreadsByRoomId(rid, [...threads], { projection: { _id: 1 } }).toArray()
|
|
).map(({ _id }) => _id);
|
|
|
|
const { modifiedCount } = await Subscriptions.removeUnreadThreadsByRoomId(rid, [...threads]);
|
|
if (modifiedCount) {
|
|
subscriptionIds.forEach((id) => notifyOnSubscriptionChangedById(id));
|
|
}
|
|
}
|
|
}
|
|
|
|
const selectedMessageIds = limit
|
|
? await Messages.findByIdPinnedTimestampLimitAndUsers(rid, excludePinned, ignoreDiscussion, ts, limit, fromUsers, ignoreThreads)
|
|
: undefined;
|
|
const count = await Messages.removeByIdPinnedTimestampLimitAndUsers(
|
|
rid,
|
|
excludePinned,
|
|
ignoreDiscussion,
|
|
ts,
|
|
limit,
|
|
fromUsers,
|
|
ignoreThreads,
|
|
selectedMessageIds,
|
|
);
|
|
|
|
if (limit && selectedMessageIds) {
|
|
await ReadReceipts.removeByMessageIds(selectedMessageIds);
|
|
await ReadReceiptsArchive.removeByMessageIds(selectedMessageIds);
|
|
}
|
|
|
|
if (count) {
|
|
const lastMessage = await Messages.getLastVisibleUserMessageSentByRoomId(rid);
|
|
|
|
await Rooms.resetLastMessageById(rid, lastMessage, -count);
|
|
|
|
void notifyOnRoomChangedById(rid);
|
|
|
|
void api.broadcast('notify.deleteMessageBulk', rid, {
|
|
rid,
|
|
excludePinned,
|
|
ignoreDiscussion,
|
|
ts,
|
|
users: fromUsers,
|
|
ids: selectedMessageIds,
|
|
});
|
|
}
|
|
|
|
return count;
|
|
}
|