mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-07-10 16:23:44 +00:00
fix: cap forced-retention edits to the message's earlier expiry
The edit/feedback path calls applyForcedRetention with capExpiryToConversation, which unconditionally set the touched message's expiredAt to the parent/fresh forced deadline. When that message already expired sooner (an older message keeps its creation-time deadline while later saves refreshed the parent's), the $set extended it past its own schedule, overwriting the earlier deadline that capForcedRetentionToParent had just preserved. Cap the touched message to min(existing expiredAt, forcedExpiredAt) via the same $min/$ifNull pipeline forceConversationMessagesTemporary already uses, so a message with a sooner deadline keeps it and a permanent message still receives the forced one.
This commit is contained in:
parent
38057713ce
commit
7a923ef404
2 changed files with 44 additions and 4 deletions
|
|
@ -1128,6 +1128,42 @@ describe('Message Operations', () => {
|
|||
}
|
||||
});
|
||||
|
||||
it('preserves an edited message expiring sooner than its parent under forced retention', async () => {
|
||||
const conversationId = uuidv4();
|
||||
const editedMessageId = uuidv4();
|
||||
const messageDeadline = new Date(Date.now() + 60 * 60 * 1000);
|
||||
const parentDeadline = new Date(Date.now() + 12 * 60 * 60 * 1000);
|
||||
await Conversation().create({
|
||||
conversationId,
|
||||
user: 'user123',
|
||||
endpoint: 'openAI',
|
||||
title: 'Carried-over chat',
|
||||
isTemporary: false,
|
||||
expiredAt: parentDeadline,
|
||||
});
|
||||
await Message.create({
|
||||
messageId: editedMessageId,
|
||||
conversationId,
|
||||
user: 'user123',
|
||||
text: 'edited',
|
||||
isTemporary: false,
|
||||
expiredAt: messageDeadline,
|
||||
});
|
||||
|
||||
await applyForcedRetention(
|
||||
{
|
||||
userId: 'user123',
|
||||
interfaceConfig: { temporaryChatRetention: 24, retentionMode: RetentionMode.EPHEMERAL },
|
||||
},
|
||||
{ conversationId, messageId: editedMessageId },
|
||||
{ context: 'edit', capExpiryToConversation: true },
|
||||
);
|
||||
|
||||
const message = await Message.findOne({ messageId: editedMessageId, user: 'user123' }).lean();
|
||||
expect(message?.isTemporary).toBe(true);
|
||||
expect(message?.expiredAt?.getTime()).toBe(messageDeadline.getTime());
|
||||
});
|
||||
|
||||
it('converts a permanent conversation and its messages without a messageId (tag write)', async () => {
|
||||
const conversationId = uuidv4();
|
||||
await Conversation().create({
|
||||
|
|
|
|||
|
|
@ -422,10 +422,14 @@ export function createMessageMethods(mongoose: typeof import('mongoose')): Messa
|
|||
}
|
||||
|
||||
if (typeof messageId === 'string' && messageId.length > 0) {
|
||||
await Message.updateOne(
|
||||
{ messageId, user: userId },
|
||||
{ $set: { isTemporary: true, expiredAt: forcedExpiredAt } },
|
||||
);
|
||||
await Message.updateOne({ messageId, user: userId }, [
|
||||
{
|
||||
$set: {
|
||||
isTemporary: true,
|
||||
expiredAt: { $min: [{ $ifNull: ['$expiredAt', forcedExpiredAt] }, forcedExpiredAt] },
|
||||
},
|
||||
},
|
||||
]);
|
||||
}
|
||||
await cascadeForcedConversationRetention(
|
||||
Conversation,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue