fix: enforce expired conversation shares in all retention

This commit is contained in:
Danny Avila 2026-05-15 14:43:11 -04:00
parent 6684f40fd4
commit f36cd25b9b
2 changed files with 51 additions and 18 deletions

View file

@ -118,6 +118,23 @@ describe('share routes retention', () => {
expect(createSharedLink).not.toHaveBeenCalled();
});
it('rejects new shares for expired conversations in all retention mode', async () => {
mongoose.models.Conversation.findOne.mockReturnValue(lean(expiredRetainedConvo));
createSharedLink.mockResolvedValue({ shareId: 'share-123' });
const response = await request(buildApp({ retentionMode: RetentionMode.ALL }))
.post('/api/share/convo-123')
.send({ targetMessageId: 'msg-123' });
expect(response.status).toBe(404);
expect(mongoose.models.Conversation.findOne).toHaveBeenCalledWith(
{ conversationId: 'convo-123', user: 'user-123' },
'isTemporary expiredAt',
);
expect(createTempChatExpirationDate).not.toHaveBeenCalled();
expect(createSharedLink).not.toHaveBeenCalled();
});
it('expires updated shares for retained non-temporary conversations', async () => {
mongoose.models.SharedLink.findOne.mockReturnValue(lean({ conversationId: 'convo-123' }));
mongoose.models.Conversation.findOne.mockReturnValue(lean(retainedConvo));
@ -154,6 +171,24 @@ describe('share routes retention', () => {
expect(updateSharedLink).not.toHaveBeenCalled();
});
it('rejects updated shares for expired conversations in all retention mode', async () => {
mongoose.models.SharedLink.findOne.mockReturnValue(lean({ conversationId: 'convo-123' }));
mongoose.models.Conversation.findOne.mockReturnValue(lean(expiredRetainedConvo));
updateSharedLink.mockResolvedValue({ shareId: 'share-456' });
const response = await request(buildApp({ retentionMode: RetentionMode.ALL })).patch(
'/api/share/share-123',
);
expect(response.status).toBe(404);
expect(mongoose.models.SharedLink.findOne).toHaveBeenCalledWith(
{ shareId: 'share-123', user: 'user-123' },
'conversationId',
);
expect(createTempChatExpirationDate).not.toHaveBeenCalled();
expect(updateSharedLink).not.toHaveBeenCalled();
});
it('clears updated share expiration when the conversation is no longer retained', async () => {
mongoose.models.SharedLink.findOne.mockReturnValue(lean({ conversationId: 'convo-123' }));
mongoose.models.Conversation.findOne.mockReturnValue(

View file

@ -21,29 +21,27 @@ const router = express.Router();
async function getSharedLinkExpiration(req, conversationId) {
const isRetentionAll = req?.config?.interfaceConfig?.retentionMode === RetentionMode.ALL;
if (!isRetentionAll) {
if (!conversationId) {
return;
}
if (!conversationId) {
return;
}
const Conversation = mongoose.models.Conversation;
const convo = await Conversation.findOne(
{ conversationId, user: req.user.id },
'isTemporary expiredAt',
).lean();
const Conversation = mongoose.models.Conversation;
const convo = await Conversation.findOne(
{ conversationId, user: req.user.id },
'isTemporary expiredAt',
).lean();
if (!convo) {
return;
}
if (!convo) {
return;
}
const conversationExpiredAt = getConversationExpirationDate(convo);
if (conversationExpiredAt == null) {
const conversationExpiredAt = getConversationExpirationDate(convo);
if (conversationExpiredAt == null) {
if (!isRetentionAll) {
return null;
}
if (!isActiveExpirationDate(conversationExpiredAt)) {
return conversationExpiredAt;
}
} else if (!isActiveExpirationDate(conversationExpiredAt)) {
return conversationExpiredAt;
}
try {