LibreChat/api/server/routes/projects.js
Marco Beretta 38057713ce
fix: enforce ephemeral retention on chat project membership changes
Project membership writes bypassed forced retention: assigning a chat to a
project, removing it, or deleting the project rewrote the conversation row
(chatProjectId) without setting isTemporary/expiredAt. On a deployment that
switched to ephemeral mode, touching an older permanent chat this way left it
visible and non-expiring.

Load the interface config on the assign and delete project routes and cascade
forced retention through the touched conversation(s): assign/remove caps the
single conversation, its messages, and its shares; delete caps every member
chat before unassigning. Extract the bucketed tag cascade into a shared helper
so the project cascade reuses the same conversion/backfill/cap logic, and add a
shared resolveForcedRetentionDate helper. All paths are a no-op outside forced
retention, so non-ephemeral behavior is unchanged.
2026-07-03 15:03:09 +02:00

29 lines
977 B
JavaScript

const express = require('express');
const { createProjectHandlers } = require('@librechat/api');
const { requireJwtAuth, configMiddleware } = require('~/server/middleware');
const db = require('~/models');
const router = express.Router();
const handlers = createProjectHandlers({
listChatProjects: db.listChatProjects,
createChatProject: db.createChatProject,
getChatProject: db.getChatProject,
updateChatProject: db.updateChatProject,
deleteChatProject: db.deleteChatProject,
assignConversationToProject: db.assignConversationToProject,
});
router.use(requireJwtAuth);
router.get('/', handlers.listProjects);
router.post('/', handlers.createProject);
router.put(
'/conversations/:conversationId',
configMiddleware,
handlers.assignConversationToProject,
);
router.get('/:projectId', handlers.getProject);
router.patch('/:projectId', handlers.updateProject);
router.delete('/:projectId', configMiddleware, handlers.deleteProject);
module.exports = router;