🗑️ chore: Remove Deprecated Project Model and Associated Fields (#11773)

* chore: remove projects and projectIds usage

* chore: empty line linting

* chore: remove isCollaborative property across agent models and related tests

- Removed the isCollaborative property from agent models, controllers, and tests, as it is deprecated in favor of ACL permissions.
- Updated related validation schemas and data provider types to reflect this change.
- Ensured all references to isCollaborative were stripped from the codebase to maintain consistency and clarity.
This commit is contained in:
Danny Avila 2026-02-13 03:04:15 -05:00
parent 3398f6a17a
commit 37cc5faff5
No known key found for this signature in database
GPG key ID: BF31EEB2C5CA0956
41 changed files with 94 additions and 821 deletions

View file

@ -173,9 +173,6 @@ const getAgentHandler = async (req, res, expandProperties = false) => {
agent.author = agent.author.toString();
// @deprecated - isCollaborative replaced by ACL permissions
agent.isCollaborative = !!agent.isCollaborative;
// Check if agent is public
const isPublic = await hasPublicPermission({
resourceType: ResourceType.AGENT,
@ -199,9 +196,6 @@ const getAgentHandler = async (req, res, expandProperties = false) => {
author: agent.author,
provider: agent.provider,
model: agent.model,
projectIds: agent.projectIds,
// @deprecated - isCollaborative replaced by ACL permissions
isCollaborative: agent.isCollaborative,
isPublic: agent.isPublic,
version: agent.version,
// Safe metadata

View file

@ -14,10 +14,6 @@ jest.mock('~/server/services/Config', () => ({
}),
}));
jest.mock('~/models/Project', () => ({
getProjectByName: jest.fn().mockResolvedValue(null),
}));
jest.mock('~/server/services/Files/strategies', () => ({
getStrategyFunctions: jest.fn(),
}));
@ -174,7 +170,6 @@ describe('Agent Controllers - Mass Assignment Protection', () => {
// Unauthorized fields that should be stripped
author: new mongoose.Types.ObjectId().toString(), // Should not be able to set author
authorName: 'Hacker', // Should be stripped
isCollaborative: true, // Should be stripped on creation
versions: [], // Should be stripped
_id: new mongoose.Types.ObjectId(), // Should be stripped
id: 'custom_agent_id', // Should be overridden
@ -193,7 +188,6 @@ describe('Agent Controllers - Mass Assignment Protection', () => {
// Verify unauthorized fields were not set
expect(createdAgent.author.toString()).toBe(mockReq.user.id); // Should be the request user, not the malicious value
expect(createdAgent.authorName).toBeUndefined();
expect(createdAgent.isCollaborative).toBeFalsy();
expect(createdAgent.versions).toHaveLength(1); // Should have exactly 1 version from creation
expect(createdAgent.id).not.toBe('custom_agent_id'); // Should have generated ID
expect(createdAgent.id).toMatch(/^agent_/); // Should have proper prefix
@ -444,7 +438,6 @@ describe('Agent Controllers - Mass Assignment Protection', () => {
model: 'gpt-3.5-turbo',
author: existingAgentAuthorId,
description: 'Original description',
isCollaborative: false,
versions: [
{
name: 'Original Agent',
@ -466,7 +459,6 @@ describe('Agent Controllers - Mass Assignment Protection', () => {
name: 'Updated Agent',
description: 'Updated description',
model: 'gpt-4',
isCollaborative: true, // This IS allowed in updates
};
await updateAgentHandler(mockReq, mockRes);
@ -479,13 +471,11 @@ describe('Agent Controllers - Mass Assignment Protection', () => {
expect(updatedAgent.name).toBe('Updated Agent');
expect(updatedAgent.description).toBe('Updated description');
expect(updatedAgent.model).toBe('gpt-4');
expect(updatedAgent.isCollaborative).toBe(true);
expect(updatedAgent.author).toBe(existingAgentAuthorId.toString());
// Verify in database
const agentInDb = await Agent.findOne({ id: existingAgentId });
expect(agentInDb.name).toBe('Updated Agent');
expect(agentInDb.isCollaborative).toBe(true);
});
test('should reject update with unauthorized fields (mass assignment protection)', async () => {
@ -540,25 +530,6 @@ describe('Agent Controllers - Mass Assignment Protection', () => {
expect(updatedAgent.name).toBe('Admin Update');
});
test('should handle projectIds updates', async () => {
mockReq.user.id = existingAgentAuthorId.toString();
mockReq.params.id = existingAgentId;
const projectId1 = new mongoose.Types.ObjectId().toString();
const projectId2 = new mongoose.Types.ObjectId().toString();
mockReq.body = {
projectIds: [projectId1, projectId2],
};
await updateAgentHandler(mockReq, mockRes);
expect(mockRes.json).toHaveBeenCalled();
const updatedAgent = mockRes.json.mock.calls[0][0];
expect(updatedAgent).toBeDefined();
});
test('should validate tool_resources in updates', async () => {
mockReq.user.id = existingAgentAuthorId.toString();
mockReq.params.id = existingAgentId;

View file

@ -21,15 +21,6 @@ const checkAgentCreate = generateCheckAccess({
getRoleByName,
});
const checkGlobalAgentShare = generateCheckAccess({
permissionType: PermissionTypes.AGENTS,
permissions: [Permissions.USE, Permissions.CREATE],
bodyProps: {
[Permissions.SHARE]: ['projectIds', 'removeProjectIds'],
},
getRoleByName,
});
router.use(requireJwtAuth);
/**
@ -99,7 +90,7 @@ router.get(
*/
router.patch(
'/:id',
checkGlobalAgentShare,
checkAgentCreate,
canAccessAgentResource({
requiredPermission: PermissionBits.EDIT,
resourceIdParam: 'id',
@ -148,7 +139,7 @@ router.delete(
*/
router.post(
'/:id/revert',
checkGlobalAgentShare,
checkAgentCreate,
canAccessAgentResource({
requiredPermission: PermissionBits.EDIT,
resourceIdParam: 'id',

View file

@ -1,10 +1,9 @@
const express = require('express');
const { logger } = require('@librechat/data-schemas');
const { isEnabled, getBalanceConfig } = require('@librechat/api');
const { Constants, CacheKeys, defaultSocialLogins } = require('librechat-data-provider');
const { CacheKeys, defaultSocialLogins } = require('librechat-data-provider');
const { getLdapConfig } = require('~/server/services/Config/ldap');
const { getAppConfig } = require('~/server/services/Config/app');
const { getProjectByName } = require('~/models/Project');
const { getLogStores } = require('~/cache');
const router = express.Router();
@ -37,8 +36,6 @@ router.get('/', async function (req, res) {
return today.getMonth() === 1 && today.getDate() === 11;
};
const instanceProject = await getProjectByName(Constants.GLOBAL_PROJECT_NAME, '_id');
const ldap = getLdapConfig();
try {
@ -101,7 +98,6 @@ router.get('/', async function (req, res) {
sharedLinksEnabled,
publicSharedLinksEnabled,
analyticsGtmId: process.env.ANALYTICS_GTM_ID,
instanceProjectId: instanceProject._id.toString(),
bundlerURL: process.env.SANDPACK_BUNDLER_URL,
staticBundlerURL: process.env.SANDPACK_STATIC_BUNDLER_URL,
sharePointFilePickerEnabled,

View file

@ -56,15 +56,6 @@ const checkPromptCreate = generateCheckAccess({
getRoleByName,
});
const checkGlobalPromptShare = generateCheckAccess({
permissionType: PermissionTypes.PROMPTS,
permissions: [Permissions.USE, Permissions.CREATE],
bodyProps: {
[Permissions.SHARE]: ['projectIds', 'removeProjectIds'],
},
getRoleByName,
});
router.use(requireJwtAuth);
router.use(checkPromptAccess);
@ -364,7 +355,7 @@ const patchPromptGroup = async (req, res) => {
router.patch(
'/groups/:groupId',
checkGlobalPromptShare,
checkPromptCreate,
canAccessPromptGroupResource({
requiredPermission: PermissionBits.EDIT,
}),

View file

@ -6,7 +6,6 @@ const {
checkAgentPermissionsMigration,
checkPromptPermissionsMigration,
} = require('@librechat/api');
const { getProjectByName } = require('~/models/Project');
const { Agent, PromptGroup } = require('~/db/models');
const { findRoleByIdentifier } = require('~/models');
@ -20,7 +19,6 @@ async function checkMigrations() {
mongoose,
methods: {
findRoleByIdentifier,
getProjectByName,
},
AgentModel: Agent,
});
@ -33,7 +31,6 @@ async function checkMigrations() {
mongoose,
methods: {
findRoleByIdentifier,
getProjectByName,
},
PromptGroupModel: PromptGroup,
});