mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-07-10 16:23:44 +00:00
fix: cap uploaded files when saveConvo forces ephemeral retention
saveConvo's inline forced-retention backfill converted the conversation and capped its messages and shared links but left File records at expiredAt: null, so archive/pin/title updates on a pre-existing chat under ephemeral mode left its uploads in storage after the conversation and messages TTL out. File cleanup only sweeps rows whose own expiredAt is set. Cap the conversation's files alongside messages and shares, matching the other forced-retention cascades.
This commit is contained in:
parent
e43364982d
commit
b871ae4b5e
2 changed files with 35 additions and 1 deletions
|
|
@ -2,7 +2,7 @@ import mongoose from 'mongoose';
|
|||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { MongoMemoryServer } from 'mongodb-memory-server';
|
||||
import { EModelEndpoint, RetentionMode } from 'librechat-data-provider';
|
||||
import type { IChatProject, IConversation, IMessage, ISharedLink } from '../types';
|
||||
import type { IChatProject, IConversation, IMessage, IMongoFile, ISharedLink } from '../types';
|
||||
import { ConversationMethods, createConversationMethods } from './conversation';
|
||||
import { tenantStorage, runAsSystem } from '~/config/tenantContext';
|
||||
import { createModels } from '../models';
|
||||
|
|
@ -851,9 +851,11 @@ describe('Conversation Operations', () => {
|
|||
|
||||
describe('forced retention message cascade', () => {
|
||||
const Message = () => mongoose.models.Message as mongoose.Model<IMessage>;
|
||||
const File = () => mongoose.models.File as mongoose.Model<IMongoFile>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await Message().deleteMany({});
|
||||
await File().deleteMany({});
|
||||
});
|
||||
|
||||
it('backfills existing messages when ephemeral converts a permanent conversation', async () => {
|
||||
|
|
@ -889,6 +891,34 @@ describe('Conversation Operations', () => {
|
|||
}
|
||||
});
|
||||
|
||||
it('caps existing files when ephemeral converts a permanent conversation', async () => {
|
||||
const conversationId = uuidv4();
|
||||
const fileId = uuidv4();
|
||||
await Conversation.create({
|
||||
conversationId,
|
||||
user: 'user123',
|
||||
endpoint: EModelEndpoint.openAI,
|
||||
title: 'Existing permanent chat',
|
||||
});
|
||||
await File().collection.insertOne({
|
||||
file_id: fileId,
|
||||
conversationId,
|
||||
user: new mongoose.Types.ObjectId(),
|
||||
expiredAt: null,
|
||||
});
|
||||
|
||||
await saveConvo(
|
||||
{
|
||||
userId: 'user123',
|
||||
interfaceConfig: { temporaryChatRetention: 24, retentionMode: RetentionMode.EPHEMERAL },
|
||||
},
|
||||
{ conversationId, isArchived: true },
|
||||
);
|
||||
|
||||
const file = await File().findOne({ file_id: fileId }).lean<IMongoFile>();
|
||||
expect(file?.expiredAt).toBeInstanceOf(Date);
|
||||
});
|
||||
|
||||
it('converts an active retained (all-mode) conversation when switching to ephemeral', async () => {
|
||||
const conversationId = uuidv4();
|
||||
const retainedUntil = new Date(Date.now() + 365 * 24 * 60 * 60 * 1000);
|
||||
|
|
|
|||
|
|
@ -6,11 +6,13 @@ import type {
|
|||
IChatProjectDocument,
|
||||
IConversation,
|
||||
IMessage,
|
||||
IMongoFile,
|
||||
ISharedLink,
|
||||
} from '~/types';
|
||||
import type { MessageMethods } from './message';
|
||||
import {
|
||||
buildRetentionVisibilityFilter,
|
||||
capConversationFiles,
|
||||
capConversationSharedLinks,
|
||||
capForcedRetentionExpiry,
|
||||
conversationNeedsForcedRetention,
|
||||
|
|
@ -365,8 +367,10 @@ export function createConversationMethods(
|
|||
) {
|
||||
const Message = mongoose.models.Message as Model<IMessage>;
|
||||
const SharedLink = mongoose.models.SharedLink as Model<ISharedLink>;
|
||||
const File = mongoose.models.File as Model<IMongoFile>;
|
||||
await forceConversationMessagesTemporary(Message, userId, conversationId, forcedExpiredAt);
|
||||
await capConversationSharedLinks(SharedLink, userId, conversationId, forcedExpiredAt);
|
||||
await capConversationFiles(File, conversationId, forcedExpiredAt);
|
||||
}
|
||||
|
||||
if (
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue