📦 refactor: Consolidate DB models, encapsulating Mongoose usage in `data-schemas` (#11830)
* chore: move database model methods to /packages/data-schemas
* chore: add TypeScript ESLint rule to warn on unused variables
* refactor: model imports to streamline access
- Consolidated model imports across various files to improve code organization and reduce redundancy.
- Updated imports for models such as Assistant, Message, Conversation, and others to a unified import path.
- Adjusted middleware and service files to reflect the new import structure, ensuring functionality remains intact.
- Enhanced test files to align with the new import paths, maintaining test coverage and integrity.
* chore: migrate database models to packages/data-schemas and refactor all direct Mongoose Model usage outside of data-schemas
* test: update agent model mocks in unit tests
- Added `getAgent` mock to `client.test.js` to enhance test coverage for agent-related functionality.
- Removed redundant `getAgent` and `getAgents` mocks from `openai.spec.js` and `responses.unit.spec.js` to streamline test setup and reduce duplication.
- Ensured consistency in agent mock implementations across test files.
* fix: update types in data-schemas
* refactor: enhance type definitions in transaction and spending methods
- Updated type definitions in `checkBalance.ts` to use specific request and response types.
- Refined `spendTokens.ts` to utilize a new `SpendTxData` interface for better clarity and type safety.
- Improved transaction handling in `transaction.ts` by introducing `TransactionResult` and `TxData` interfaces, ensuring consistent data structures across methods.
- Adjusted unit tests in `transaction.spec.ts` to accommodate new type definitions and enhance robustness.
* refactor: streamline model imports and enhance code organization
- Consolidated model imports across various controllers and services to a unified import path, improving code clarity and reducing redundancy.
- Updated multiple files to reflect the new import structure, ensuring all functionalities remain intact.
- Enhanced overall code organization by removing duplicate import statements and optimizing the usage of model methods.
* feat: implement loadAddedAgent and refactor agent loading logic
- Introduced `loadAddedAgent` function to handle loading agents from added conversations, supporting multi-convo parallel execution.
- Created a new `load.ts` file to encapsulate agent loading functionalities, including `loadEphemeralAgent` and `loadAgent`.
- Updated the `index.ts` file to export the new `load` module instead of the deprecated `loadAgent`.
- Enhanced type definitions and improved error handling in the agent loading process.
- Adjusted unit tests to reflect changes in the agent loading structure and ensure comprehensive coverage.
* refactor: enhance balance handling with new update interface
- Introduced `IBalanceUpdate` interface to streamline balance update operations across the codebase.
- Updated `upsertBalanceFields` method signatures in `balance.ts`, `transaction.ts`, and related tests to utilize the new interface for improved type safety.
- Adjusted type imports in `balance.spec.ts` to include `IBalanceUpdate`, ensuring consistency in balance management functionalities.
- Enhanced overall code clarity and maintainability by refining type definitions related to balance operations.
* feat: add unit tests for loadAgent functionality and enhance agent loading logic
- Introduced comprehensive unit tests for the `loadAgent` function, covering various scenarios including null and empty agent IDs, loading of ephemeral agents, and permission checks.
- Enhanced the `initializeClient` function by moving `getConvoFiles` to the correct position in the database method exports, ensuring proper functionality.
- Improved test coverage for agent loading, including handling of non-existent agents and user permissions.
* chore: reorder memory method exports for consistency
- Moved `deleteAllUserMemories` to the correct position in the exported memory methods, ensuring a consistent and logical order of method exports in `memory.ts`.
2026-02-17 18:23:44 -05:00
|
|
|
import mongoose from 'mongoose';
|
|
|
|
|
import { MongoMemoryServer } from 'mongodb-memory-server';
|
|
|
|
|
import { SystemRoles, Permissions, roleDefaults, PermissionTypes } from 'librechat-data-provider';
|
🪪 feat: Admin Roles API Endpoints (#12400)
* feat: add createRole and deleteRole methods to role
* feat: add admin roles handler factory and Express routes
* fix: address convention violations in admin roles handlers
* fix: rename createRole/deleteRole to avoid AccessRole name collision
The existing accessRole.ts already exports createRole/deleteRole for the
AccessRole model. In createMethods index.ts, these are spread after
roleMethods, overwriting them. Renamed our Role methods to
createRoleByName/deleteRoleByName to match the existing pattern
(getRoleByName, updateRoleByName) and avoid the collision.
* feat: add description field to Role model
- Add description to IRole, CreateRoleRequest, UpdateRoleRequest types
- Add description field to Mongoose roleSchema (default: '')
- Wire description through createRoleHandler and updateRoleHandler
- Include description in listRoles select clause so it appears in list
* fix: address Copilot review findings in admin roles handlers
* test: add unit tests for admin roles and groups handlers
* test: add data-layer tests for createRoleByName, deleteRoleByName, listUsersByRole
* fix: allow system role updates when name is unchanged
The updateRoleHandler guard rejected any request where body.name matched
a system role, even when the name was not being changed. This blocked
editing a system role's description. Compare against the URL param to
only reject actual renames to reserved names.
* fix: address external review findings for admin roles
- Block renaming system roles (ADMIN/USER) and add user migration on rename
- Add input validation: name max-length, trim on update, duplicate name check
- Replace fragile String.includes error matching with prefix-based classification
- Catch MongoDB 11000 duplicate key in createRoleByName
- Add pagination (limit/offset/total) to getRoleMembersHandler
- Reverse delete order in deleteRoleByName — reassign users before deletion
- Add role existence check in removeRoleMember; drop unused createdAt select
- Add Array.isArray guard for permissions input; use consistent ?? coalescing
- Fix import ordering per AGENTS.md conventions
- Type-cast mongoose.models.User as Model<IUser> for proper TS inference
- Add comprehensive tests: rename guards, pagination, validation, 500 paths
* fix: address re-review findings for admin roles
- Gate deleteRoleByName on existence check — skip user reassignment and
cache invalidation when role doesn't exist (fixes test mismatch)
- Reverse rename order: migrate users before renaming role so a migration
failure leaves the system in a consistent state
- Add .sort({ _id: 1 }) to listUsersByRole for deterministic pagination
- Import shared AdminMember type from data-schemas instead of local copy;
make joinedAt optional since neither groups nor roles populate it
- Change IRole.description from optional to required to match schema default
- Add data-layer tests for updateUsersByRole and countUsersByRole
- Add handler test verifying users-first rename ordering and migration
failure safety
* fix: add rollback on rename failure and update PR description
- Roll back user migration if updateRoleByName returns null during a
rename (race: role deleted between existence check and update)
- Add test verifying rollback calls updateUsersByRole in reverse
- Update PR #12400 description to reflect current test counts (56
handler tests, 40 data-layer tests) and safety features
* fix: rollback on rename throw, description validation, delete/DRY cleanup
- Hoist isRename/trimmedName above try block so catch can roll back user
migration when updateRoleByName throws (not just returns null)
- Add description type + max-length (2000) validation in create and update,
consistent with groups handler
- Remove redundant getRoleByName existence check in deleteRoleHandler —
use deleteRoleByName return value directly
- Skip no-op name write when body.name equals current name (use isRename)
- Extract getUserModel() accessor to DRY repeated Model<IUser> casts
- Use name.trim() consistently in createRoleByName error messages
- Add tests: rename-throw rollback, description validation (create+update),
update delete test mocks to match simplified handler
* fix: guard spurious rollback, harden createRole error path, validate before DB calls
- Add migrationRan flag to prevent rollback of user migration that never ran
- Return generic message on 500 in createRoleHandler, specific only for 409
- Move description validation before DB queries in updateRoleHandler
- Return existing role early when update body has no changes
- Wrap cache.set in createRoleByName with try/catch to prevent masking DB success
- Add JSDoc on 11000 catch explaining compound unique index
- Add tests: spurious rollback guard, empty update body, description validation
ordering, listUsersByRole pagination
* fix: validate permissions in create, RoleConflictError, rollback safety, cache consistency
- Add permissions type/array validation in createRoleHandler
- Introduce RoleConflictError class replacing fragile string-prefix matching
- Wrap rollback in !role null path with try/catch for correct 404 response
- Wrap deleteRoleByName cache.set in try/catch matching createRoleByName
- Narrow updateRoleHandler body type to { name?, description? }
- Add tests: non-string description in create, rollback failure logging,
permissions array rejection, description max-length assertion fix
* feat: prevent removing the last admin user
Add guard in removeRoleMember that checks countUsersByRole before
demoting an ADMIN user, returning 400 if they are the last one.
* fix: move interleaved export below imports, add await to countUsersByRole
* fix: paginate listRoles, null-guard permissions handler, fix export ordering
- Add limit/offset/total pagination to listRoles matching the groups pattern
- Add countRoles data-layer method
- Omit permissions from listRoles select (getRole returns full document)
- Null-guard re-fetched role in updateRolePermissionsHandler
- Move interleaved export below all imports in methods/index.ts
* fix: address review findings — race safety, validation DRY, type accuracy, test coverage
- Add post-write admin count verification in removeRoleMember to prevent
zero-admin race condition (TOCTOU → rollback if count hits 0)
- Make IRole.description optional; backfill in initializeRoles for
pre-existing roles that lack the field (.lean() bypasses defaults)
- Extract parsePagination, validateNameParam, validateRoleName, and
validateDescription helpers to eliminate duplicated validation
- Add validateNameParam guard to all 7 handlers reading req.params.name
- Catch 11000 in updateRoleByName and surface as 409 via RoleConflictError
- Add idempotent skip in addRoleMember when user already has target role
- Verify updateRolePermissions test asserts response body
- Add data-layer tests: listRoles sort/pagination/projection, countRoles,
and createRoleByName 11000 duplicate key race
* fix: defensive rollback in removeRoleMember, type/style cleanup, test coverage
- Wrap removeRoleMember post-write admin rollback in try/catch so a
transient DB failure cannot leave the system with zero administrators
- Replace double `as unknown[] as IRole[]` cast with `.lean<IRole[]>()`
- Type parsePagination param explicitly; extract DEFAULT/MAX page constants
- Preserve original error cause in updateRoleByName re-throw
- Add test for rollback failure path in removeRoleMember (returns 400)
- Add test for pre-existing roles missing description field (.lean())
* chore: bump @librechat/data-schemas to 0.0.47
* fix: stale cache on rename, extract renameRole helper, shared pagination, cleanup
- Fix updateRoleByName cache bug: invalidate old key and populate new key
when updates.name differs from roleName (prevents stale cache after rename)
- Extract renameRole helper to eliminate mutable outer-scope state flags
(isRename, trimmedName, migrationRan) in updateRoleHandler
- Unify system-role protection to 403 for both rename-from and rename-to
- Extract parsePagination to shared admin/pagination.ts; use in both
roles.ts and groups.ts
- Extract name.trim() to local const in createRoleByName (was called 5×)
- Remove redundant findOne pre-check in deleteRoleByName
- Replace getUserModel closure with local const declarations
- Remove redundant description ?? '' in createRoleHandler (schema default)
- Add doc comment on updateRolePermissionsHandler noting cache dependency
- Add data-layer tests for cache rename behavior (old key null, new key set)
* fix: harden role guards, add User.role index, validate names, improve tests
- Add index on User.role field for efficient member queries at scale
- Replace fragile SystemRoles key lookup with value-based Set check (6 sites)
- Elevate rename rollback failure logging to CRITICAL (matches removeRoleMember)
- Guard removeRoleMember against non-ADMIN system roles (403 for USER)
- Fix parsePagination limit=0 gotcha: use parseInt + NaN check instead of ||
- Add control character and reserved path segment validation to role names
- Simplify validateRoleName: remove redundant casts and dead conditions
- Add JSDoc to deleteRoleByName documenting non-atomic window
- Split mixed value+type import in methods/index.ts per AGENTS.md
- Add 9 new tests: permissions assertion, combined rename+desc, createRole
with permissions, pagination edge cases, control char/reserved name
rejection, system role removeRoleMember guard
* fix: exact-case reserved name check, consistent validation, cleaner createRole
- Remove .toLowerCase() from reserved name check so only exact matches
(members, permissions) are rejected, not legitimate names like "Members"
- Extract trimmed const in validateRoleName for consistent validation
- Add control char check to validateNameParam for parity with body validation
- Build createRole roleData conditionally to avoid passing description: undefined
- Expand deleteRoleByName JSDoc documenting self-healing design and no-op trade-off
* fix: scope rename rollback to only migrated users, prevent cross-role corruption
Capture user IDs before forward migration so the rollback path only
reverts users this request actually moved. Previously the rollback called
updateUsersByRole(newName, currentName) which would sweep all users with
the new role — including any independently assigned by a concurrent admin
request — causing silent cross-role data corruption.
Adds findUserIdsByRole and updateUsersRoleByIds to the data layer.
Extracts rollbackMigratedUsers helper to deduplicate rollback sites.
* fix: guard last admin in addRoleMember to prevent zero-admin lockout
Since each user has exactly one role, addRoleMember implicitly removes
the user from their current role. Without a guard, reassigning the sole
admin to a non-admin role leaves zero admins and locks out admin
management. Adds the same countUsersByRole check used in removeRoleMember.
* fix: wire findUserIdsByRole and updateUsersRoleByIds into roles route
The scoped rollback deps added in c89b5db were missing from the route
DI wiring, causing renameRole to call undefined and return a 500.
* fix: post-write admin guard in addRoleMember, compound role index, review cleanup
- Add post-write admin count check + rollback to addRoleMember to match
removeRoleMember's two-phase TOCTOU protection (prevents zero-admin via
concurrent requests)
- Replace single-field User.role index with compound { role: 1, tenantId: 1 }
to align with existing multi-tenant index pattern (email, OAuth IDs)
- Narrow listRoles dep return type to RoleListItem (projected fields only)
- Refactor validateDescription to early-return style per AGENTS.md
- Remove redundant double .lean() in updateRoleByName
- Document rename snapshot race window in renameRole JSDoc
- Document cache null-set behavior in deleteRoleByName
- Add routing-coupling comment on RESERVED_ROLE_NAMES
- Add test for addRoleMember post-write rollback
* fix: review cleanup — system-role guard, type safety, JSDoc accuracy, tests
- Add system-role guard to addRoleMember: block direct assignment to
non-ADMIN system roles (403), symmetric with removeRoleMember
- Fix RESERVED_ROLE_NAMES comment: explain semantic URL ambiguity, not
a routing conflict (Express resolves single vs multi-segment correctly)
- Replace _id: unknown with Types.ObjectId | string per AGENTS.md
- Narrow listRoles data-layer return type to Pick<IRole, 'name' | 'description'>
to match the actual .select() projection
- Move updateRoleHandler param check inside try/catch for consistency
- Include user IDs in all CRITICAL rollback failure logs for operator recovery
- Clarify deleteRoleByName JSDoc: replace "self-healing" with "idempotent",
document that recovery requires caller retry
- Add tests: system-role guard, promote non-admin to ADMIN,
findUserIdsByRole throw prevents migration
* fix: include _id in listRoles return type to match RoleListItem
Pick<IRole, 'name' | 'description'> omits _id, making it incompatible
with the handler dep's RoleListItem which requires _id.
* fix: case-insensitive system role guard, reject null permissions, check updateUser result
- System role name checks now use case-insensitive comparison via
toUpperCase() — prevents creating 'admin' or 'user' which would
collide with the legacy roles route that uppercases params
- Reject permissions: null in createRole (typeof null === 'object'
was bypassing the validation)
- Check updateUser return in addRoleMember — return 404 if the user
was deleted between the findUser and updateUser calls
* fix: check updateUser return in removeRoleMember for concurrent delete safety
---------
Co-authored-by: Danny Avila <danny@librechat.ai>
2026-03-27 12:44:47 -07:00
|
|
|
import type { IRole, IUser, RolePermissions } from '..';
|
📦 refactor: Consolidate DB models, encapsulating Mongoose usage in `data-schemas` (#11830)
* chore: move database model methods to /packages/data-schemas
* chore: add TypeScript ESLint rule to warn on unused variables
* refactor: model imports to streamline access
- Consolidated model imports across various files to improve code organization and reduce redundancy.
- Updated imports for models such as Assistant, Message, Conversation, and others to a unified import path.
- Adjusted middleware and service files to reflect the new import structure, ensuring functionality remains intact.
- Enhanced test files to align with the new import paths, maintaining test coverage and integrity.
* chore: migrate database models to packages/data-schemas and refactor all direct Mongoose Model usage outside of data-schemas
* test: update agent model mocks in unit tests
- Added `getAgent` mock to `client.test.js` to enhance test coverage for agent-related functionality.
- Removed redundant `getAgent` and `getAgents` mocks from `openai.spec.js` and `responses.unit.spec.js` to streamline test setup and reduce duplication.
- Ensured consistency in agent mock implementations across test files.
* fix: update types in data-schemas
* refactor: enhance type definitions in transaction and spending methods
- Updated type definitions in `checkBalance.ts` to use specific request and response types.
- Refined `spendTokens.ts` to utilize a new `SpendTxData` interface for better clarity and type safety.
- Improved transaction handling in `transaction.ts` by introducing `TransactionResult` and `TxData` interfaces, ensuring consistent data structures across methods.
- Adjusted unit tests in `transaction.spec.ts` to accommodate new type definitions and enhance robustness.
* refactor: streamline model imports and enhance code organization
- Consolidated model imports across various controllers and services to a unified import path, improving code clarity and reducing redundancy.
- Updated multiple files to reflect the new import structure, ensuring all functionalities remain intact.
- Enhanced overall code organization by removing duplicate import statements and optimizing the usage of model methods.
* feat: implement loadAddedAgent and refactor agent loading logic
- Introduced `loadAddedAgent` function to handle loading agents from added conversations, supporting multi-convo parallel execution.
- Created a new `load.ts` file to encapsulate agent loading functionalities, including `loadEphemeralAgent` and `loadAgent`.
- Updated the `index.ts` file to export the new `load` module instead of the deprecated `loadAgent`.
- Enhanced type definitions and improved error handling in the agent loading process.
- Adjusted unit tests to reflect changes in the agent loading structure and ensure comprehensive coverage.
* refactor: enhance balance handling with new update interface
- Introduced `IBalanceUpdate` interface to streamline balance update operations across the codebase.
- Updated `upsertBalanceFields` method signatures in `balance.ts`, `transaction.ts`, and related tests to utilize the new interface for improved type safety.
- Adjusted type imports in `balance.spec.ts` to include `IBalanceUpdate`, ensuring consistency in balance management functionalities.
- Enhanced overall code clarity and maintainability by refining type definitions related to balance operations.
* feat: add unit tests for loadAgent functionality and enhance agent loading logic
- Introduced comprehensive unit tests for the `loadAgent` function, covering various scenarios including null and empty agent IDs, loading of ephemeral agents, and permission checks.
- Enhanced the `initializeClient` function by moving `getConvoFiles` to the correct position in the database method exports, ensuring proper functionality.
- Improved test coverage for agent loading, including handling of non-existent agents and user permissions.
* chore: reorder memory method exports for consistency
- Moved `deleteAllUserMemories` to the correct position in the exported memory methods, ensuring a consistent and logical order of method exports in `memory.ts`.
2026-02-17 18:23:44 -05:00
|
|
|
import { createRoleMethods } from './role';
|
|
|
|
|
import { createModels } from '../models';
|
|
|
|
|
|
🪪 feat: Admin Roles API Endpoints (#12400)
* feat: add createRole and deleteRole methods to role
* feat: add admin roles handler factory and Express routes
* fix: address convention violations in admin roles handlers
* fix: rename createRole/deleteRole to avoid AccessRole name collision
The existing accessRole.ts already exports createRole/deleteRole for the
AccessRole model. In createMethods index.ts, these are spread after
roleMethods, overwriting them. Renamed our Role methods to
createRoleByName/deleteRoleByName to match the existing pattern
(getRoleByName, updateRoleByName) and avoid the collision.
* feat: add description field to Role model
- Add description to IRole, CreateRoleRequest, UpdateRoleRequest types
- Add description field to Mongoose roleSchema (default: '')
- Wire description through createRoleHandler and updateRoleHandler
- Include description in listRoles select clause so it appears in list
* fix: address Copilot review findings in admin roles handlers
* test: add unit tests for admin roles and groups handlers
* test: add data-layer tests for createRoleByName, deleteRoleByName, listUsersByRole
* fix: allow system role updates when name is unchanged
The updateRoleHandler guard rejected any request where body.name matched
a system role, even when the name was not being changed. This blocked
editing a system role's description. Compare against the URL param to
only reject actual renames to reserved names.
* fix: address external review findings for admin roles
- Block renaming system roles (ADMIN/USER) and add user migration on rename
- Add input validation: name max-length, trim on update, duplicate name check
- Replace fragile String.includes error matching with prefix-based classification
- Catch MongoDB 11000 duplicate key in createRoleByName
- Add pagination (limit/offset/total) to getRoleMembersHandler
- Reverse delete order in deleteRoleByName — reassign users before deletion
- Add role existence check in removeRoleMember; drop unused createdAt select
- Add Array.isArray guard for permissions input; use consistent ?? coalescing
- Fix import ordering per AGENTS.md conventions
- Type-cast mongoose.models.User as Model<IUser> for proper TS inference
- Add comprehensive tests: rename guards, pagination, validation, 500 paths
* fix: address re-review findings for admin roles
- Gate deleteRoleByName on existence check — skip user reassignment and
cache invalidation when role doesn't exist (fixes test mismatch)
- Reverse rename order: migrate users before renaming role so a migration
failure leaves the system in a consistent state
- Add .sort({ _id: 1 }) to listUsersByRole for deterministic pagination
- Import shared AdminMember type from data-schemas instead of local copy;
make joinedAt optional since neither groups nor roles populate it
- Change IRole.description from optional to required to match schema default
- Add data-layer tests for updateUsersByRole and countUsersByRole
- Add handler test verifying users-first rename ordering and migration
failure safety
* fix: add rollback on rename failure and update PR description
- Roll back user migration if updateRoleByName returns null during a
rename (race: role deleted between existence check and update)
- Add test verifying rollback calls updateUsersByRole in reverse
- Update PR #12400 description to reflect current test counts (56
handler tests, 40 data-layer tests) and safety features
* fix: rollback on rename throw, description validation, delete/DRY cleanup
- Hoist isRename/trimmedName above try block so catch can roll back user
migration when updateRoleByName throws (not just returns null)
- Add description type + max-length (2000) validation in create and update,
consistent with groups handler
- Remove redundant getRoleByName existence check in deleteRoleHandler —
use deleteRoleByName return value directly
- Skip no-op name write when body.name equals current name (use isRename)
- Extract getUserModel() accessor to DRY repeated Model<IUser> casts
- Use name.trim() consistently in createRoleByName error messages
- Add tests: rename-throw rollback, description validation (create+update),
update delete test mocks to match simplified handler
* fix: guard spurious rollback, harden createRole error path, validate before DB calls
- Add migrationRan flag to prevent rollback of user migration that never ran
- Return generic message on 500 in createRoleHandler, specific only for 409
- Move description validation before DB queries in updateRoleHandler
- Return existing role early when update body has no changes
- Wrap cache.set in createRoleByName with try/catch to prevent masking DB success
- Add JSDoc on 11000 catch explaining compound unique index
- Add tests: spurious rollback guard, empty update body, description validation
ordering, listUsersByRole pagination
* fix: validate permissions in create, RoleConflictError, rollback safety, cache consistency
- Add permissions type/array validation in createRoleHandler
- Introduce RoleConflictError class replacing fragile string-prefix matching
- Wrap rollback in !role null path with try/catch for correct 404 response
- Wrap deleteRoleByName cache.set in try/catch matching createRoleByName
- Narrow updateRoleHandler body type to { name?, description? }
- Add tests: non-string description in create, rollback failure logging,
permissions array rejection, description max-length assertion fix
* feat: prevent removing the last admin user
Add guard in removeRoleMember that checks countUsersByRole before
demoting an ADMIN user, returning 400 if they are the last one.
* fix: move interleaved export below imports, add await to countUsersByRole
* fix: paginate listRoles, null-guard permissions handler, fix export ordering
- Add limit/offset/total pagination to listRoles matching the groups pattern
- Add countRoles data-layer method
- Omit permissions from listRoles select (getRole returns full document)
- Null-guard re-fetched role in updateRolePermissionsHandler
- Move interleaved export below all imports in methods/index.ts
* fix: address review findings — race safety, validation DRY, type accuracy, test coverage
- Add post-write admin count verification in removeRoleMember to prevent
zero-admin race condition (TOCTOU → rollback if count hits 0)
- Make IRole.description optional; backfill in initializeRoles for
pre-existing roles that lack the field (.lean() bypasses defaults)
- Extract parsePagination, validateNameParam, validateRoleName, and
validateDescription helpers to eliminate duplicated validation
- Add validateNameParam guard to all 7 handlers reading req.params.name
- Catch 11000 in updateRoleByName and surface as 409 via RoleConflictError
- Add idempotent skip in addRoleMember when user already has target role
- Verify updateRolePermissions test asserts response body
- Add data-layer tests: listRoles sort/pagination/projection, countRoles,
and createRoleByName 11000 duplicate key race
* fix: defensive rollback in removeRoleMember, type/style cleanup, test coverage
- Wrap removeRoleMember post-write admin rollback in try/catch so a
transient DB failure cannot leave the system with zero administrators
- Replace double `as unknown[] as IRole[]` cast with `.lean<IRole[]>()`
- Type parsePagination param explicitly; extract DEFAULT/MAX page constants
- Preserve original error cause in updateRoleByName re-throw
- Add test for rollback failure path in removeRoleMember (returns 400)
- Add test for pre-existing roles missing description field (.lean())
* chore: bump @librechat/data-schemas to 0.0.47
* fix: stale cache on rename, extract renameRole helper, shared pagination, cleanup
- Fix updateRoleByName cache bug: invalidate old key and populate new key
when updates.name differs from roleName (prevents stale cache after rename)
- Extract renameRole helper to eliminate mutable outer-scope state flags
(isRename, trimmedName, migrationRan) in updateRoleHandler
- Unify system-role protection to 403 for both rename-from and rename-to
- Extract parsePagination to shared admin/pagination.ts; use in both
roles.ts and groups.ts
- Extract name.trim() to local const in createRoleByName (was called 5×)
- Remove redundant findOne pre-check in deleteRoleByName
- Replace getUserModel closure with local const declarations
- Remove redundant description ?? '' in createRoleHandler (schema default)
- Add doc comment on updateRolePermissionsHandler noting cache dependency
- Add data-layer tests for cache rename behavior (old key null, new key set)
* fix: harden role guards, add User.role index, validate names, improve tests
- Add index on User.role field for efficient member queries at scale
- Replace fragile SystemRoles key lookup with value-based Set check (6 sites)
- Elevate rename rollback failure logging to CRITICAL (matches removeRoleMember)
- Guard removeRoleMember against non-ADMIN system roles (403 for USER)
- Fix parsePagination limit=0 gotcha: use parseInt + NaN check instead of ||
- Add control character and reserved path segment validation to role names
- Simplify validateRoleName: remove redundant casts and dead conditions
- Add JSDoc to deleteRoleByName documenting non-atomic window
- Split mixed value+type import in methods/index.ts per AGENTS.md
- Add 9 new tests: permissions assertion, combined rename+desc, createRole
with permissions, pagination edge cases, control char/reserved name
rejection, system role removeRoleMember guard
* fix: exact-case reserved name check, consistent validation, cleaner createRole
- Remove .toLowerCase() from reserved name check so only exact matches
(members, permissions) are rejected, not legitimate names like "Members"
- Extract trimmed const in validateRoleName for consistent validation
- Add control char check to validateNameParam for parity with body validation
- Build createRole roleData conditionally to avoid passing description: undefined
- Expand deleteRoleByName JSDoc documenting self-healing design and no-op trade-off
* fix: scope rename rollback to only migrated users, prevent cross-role corruption
Capture user IDs before forward migration so the rollback path only
reverts users this request actually moved. Previously the rollback called
updateUsersByRole(newName, currentName) which would sweep all users with
the new role — including any independently assigned by a concurrent admin
request — causing silent cross-role data corruption.
Adds findUserIdsByRole and updateUsersRoleByIds to the data layer.
Extracts rollbackMigratedUsers helper to deduplicate rollback sites.
* fix: guard last admin in addRoleMember to prevent zero-admin lockout
Since each user has exactly one role, addRoleMember implicitly removes
the user from their current role. Without a guard, reassigning the sole
admin to a non-admin role leaves zero admins and locks out admin
management. Adds the same countUsersByRole check used in removeRoleMember.
* fix: wire findUserIdsByRole and updateUsersRoleByIds into roles route
The scoped rollback deps added in c89b5db were missing from the route
DI wiring, causing renameRole to call undefined and return a 500.
* fix: post-write admin guard in addRoleMember, compound role index, review cleanup
- Add post-write admin count check + rollback to addRoleMember to match
removeRoleMember's two-phase TOCTOU protection (prevents zero-admin via
concurrent requests)
- Replace single-field User.role index with compound { role: 1, tenantId: 1 }
to align with existing multi-tenant index pattern (email, OAuth IDs)
- Narrow listRoles dep return type to RoleListItem (projected fields only)
- Refactor validateDescription to early-return style per AGENTS.md
- Remove redundant double .lean() in updateRoleByName
- Document rename snapshot race window in renameRole JSDoc
- Document cache null-set behavior in deleteRoleByName
- Add routing-coupling comment on RESERVED_ROLE_NAMES
- Add test for addRoleMember post-write rollback
* fix: review cleanup — system-role guard, type safety, JSDoc accuracy, tests
- Add system-role guard to addRoleMember: block direct assignment to
non-ADMIN system roles (403), symmetric with removeRoleMember
- Fix RESERVED_ROLE_NAMES comment: explain semantic URL ambiguity, not
a routing conflict (Express resolves single vs multi-segment correctly)
- Replace _id: unknown with Types.ObjectId | string per AGENTS.md
- Narrow listRoles data-layer return type to Pick<IRole, 'name' | 'description'>
to match the actual .select() projection
- Move updateRoleHandler param check inside try/catch for consistency
- Include user IDs in all CRITICAL rollback failure logs for operator recovery
- Clarify deleteRoleByName JSDoc: replace "self-healing" with "idempotent",
document that recovery requires caller retry
- Add tests: system-role guard, promote non-admin to ADMIN,
findUserIdsByRole throw prevents migration
* fix: include _id in listRoles return type to match RoleListItem
Pick<IRole, 'name' | 'description'> omits _id, making it incompatible
with the handler dep's RoleListItem which requires _id.
* fix: case-insensitive system role guard, reject null permissions, check updateUser result
- System role name checks now use case-insensitive comparison via
toUpperCase() — prevents creating 'admin' or 'user' which would
collide with the legacy roles route that uppercases params
- Reject permissions: null in createRole (typeof null === 'object'
was bypassing the validation)
- Check updateUser return in addRoleMember — return 404 if the user
was deleted between the findUser and updateUser calls
* fix: check updateUser return in removeRoleMember for concurrent delete safety
---------
Co-authored-by: Danny Avila <danny@librechat.ai>
2026-03-27 12:44:47 -07:00
|
|
|
jest.mock('~/config/winston', () => ({
|
|
|
|
|
error: jest.fn(),
|
|
|
|
|
warn: jest.fn(),
|
|
|
|
|
info: jest.fn(),
|
|
|
|
|
debug: jest.fn(),
|
|
|
|
|
}));
|
|
|
|
|
|
📦 refactor: Consolidate DB models, encapsulating Mongoose usage in `data-schemas` (#11830)
* chore: move database model methods to /packages/data-schemas
* chore: add TypeScript ESLint rule to warn on unused variables
* refactor: model imports to streamline access
- Consolidated model imports across various files to improve code organization and reduce redundancy.
- Updated imports for models such as Assistant, Message, Conversation, and others to a unified import path.
- Adjusted middleware and service files to reflect the new import structure, ensuring functionality remains intact.
- Enhanced test files to align with the new import paths, maintaining test coverage and integrity.
* chore: migrate database models to packages/data-schemas and refactor all direct Mongoose Model usage outside of data-schemas
* test: update agent model mocks in unit tests
- Added `getAgent` mock to `client.test.js` to enhance test coverage for agent-related functionality.
- Removed redundant `getAgent` and `getAgents` mocks from `openai.spec.js` and `responses.unit.spec.js` to streamline test setup and reduce duplication.
- Ensured consistency in agent mock implementations across test files.
* fix: update types in data-schemas
* refactor: enhance type definitions in transaction and spending methods
- Updated type definitions in `checkBalance.ts` to use specific request and response types.
- Refined `spendTokens.ts` to utilize a new `SpendTxData` interface for better clarity and type safety.
- Improved transaction handling in `transaction.ts` by introducing `TransactionResult` and `TxData` interfaces, ensuring consistent data structures across methods.
- Adjusted unit tests in `transaction.spec.ts` to accommodate new type definitions and enhance robustness.
* refactor: streamline model imports and enhance code organization
- Consolidated model imports across various controllers and services to a unified import path, improving code clarity and reducing redundancy.
- Updated multiple files to reflect the new import structure, ensuring all functionalities remain intact.
- Enhanced overall code organization by removing duplicate import statements and optimizing the usage of model methods.
* feat: implement loadAddedAgent and refactor agent loading logic
- Introduced `loadAddedAgent` function to handle loading agents from added conversations, supporting multi-convo parallel execution.
- Created a new `load.ts` file to encapsulate agent loading functionalities, including `loadEphemeralAgent` and `loadAgent`.
- Updated the `index.ts` file to export the new `load` module instead of the deprecated `loadAgent`.
- Enhanced type definitions and improved error handling in the agent loading process.
- Adjusted unit tests to reflect changes in the agent loading structure and ensure comprehensive coverage.
* refactor: enhance balance handling with new update interface
- Introduced `IBalanceUpdate` interface to streamline balance update operations across the codebase.
- Updated `upsertBalanceFields` method signatures in `balance.ts`, `transaction.ts`, and related tests to utilize the new interface for improved type safety.
- Adjusted type imports in `balance.spec.ts` to include `IBalanceUpdate`, ensuring consistency in balance management functionalities.
- Enhanced overall code clarity and maintainability by refining type definitions related to balance operations.
* feat: add unit tests for loadAgent functionality and enhance agent loading logic
- Introduced comprehensive unit tests for the `loadAgent` function, covering various scenarios including null and empty agent IDs, loading of ephemeral agents, and permission checks.
- Enhanced the `initializeClient` function by moving `getConvoFiles` to the correct position in the database method exports, ensuring proper functionality.
- Improved test coverage for agent loading, including handling of non-existent agents and user permissions.
* chore: reorder memory method exports for consistency
- Moved `deleteAllUserMemories` to the correct position in the exported memory methods, ensuring a consistent and logical order of method exports in `memory.ts`.
2026-02-17 18:23:44 -05:00
|
|
|
const mockCache = {
|
|
|
|
|
get: jest.fn(),
|
|
|
|
|
set: jest.fn(),
|
|
|
|
|
del: jest.fn(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const mockGetCache = jest.fn().mockReturnValue(mockCache);
|
|
|
|
|
|
|
|
|
|
let Role: mongoose.Model<IRole>;
|
🪪 feat: Admin Roles API Endpoints (#12400)
* feat: add createRole and deleteRole methods to role
* feat: add admin roles handler factory and Express routes
* fix: address convention violations in admin roles handlers
* fix: rename createRole/deleteRole to avoid AccessRole name collision
The existing accessRole.ts already exports createRole/deleteRole for the
AccessRole model. In createMethods index.ts, these are spread after
roleMethods, overwriting them. Renamed our Role methods to
createRoleByName/deleteRoleByName to match the existing pattern
(getRoleByName, updateRoleByName) and avoid the collision.
* feat: add description field to Role model
- Add description to IRole, CreateRoleRequest, UpdateRoleRequest types
- Add description field to Mongoose roleSchema (default: '')
- Wire description through createRoleHandler and updateRoleHandler
- Include description in listRoles select clause so it appears in list
* fix: address Copilot review findings in admin roles handlers
* test: add unit tests for admin roles and groups handlers
* test: add data-layer tests for createRoleByName, deleteRoleByName, listUsersByRole
* fix: allow system role updates when name is unchanged
The updateRoleHandler guard rejected any request where body.name matched
a system role, even when the name was not being changed. This blocked
editing a system role's description. Compare against the URL param to
only reject actual renames to reserved names.
* fix: address external review findings for admin roles
- Block renaming system roles (ADMIN/USER) and add user migration on rename
- Add input validation: name max-length, trim on update, duplicate name check
- Replace fragile String.includes error matching with prefix-based classification
- Catch MongoDB 11000 duplicate key in createRoleByName
- Add pagination (limit/offset/total) to getRoleMembersHandler
- Reverse delete order in deleteRoleByName — reassign users before deletion
- Add role existence check in removeRoleMember; drop unused createdAt select
- Add Array.isArray guard for permissions input; use consistent ?? coalescing
- Fix import ordering per AGENTS.md conventions
- Type-cast mongoose.models.User as Model<IUser> for proper TS inference
- Add comprehensive tests: rename guards, pagination, validation, 500 paths
* fix: address re-review findings for admin roles
- Gate deleteRoleByName on existence check — skip user reassignment and
cache invalidation when role doesn't exist (fixes test mismatch)
- Reverse rename order: migrate users before renaming role so a migration
failure leaves the system in a consistent state
- Add .sort({ _id: 1 }) to listUsersByRole for deterministic pagination
- Import shared AdminMember type from data-schemas instead of local copy;
make joinedAt optional since neither groups nor roles populate it
- Change IRole.description from optional to required to match schema default
- Add data-layer tests for updateUsersByRole and countUsersByRole
- Add handler test verifying users-first rename ordering and migration
failure safety
* fix: add rollback on rename failure and update PR description
- Roll back user migration if updateRoleByName returns null during a
rename (race: role deleted between existence check and update)
- Add test verifying rollback calls updateUsersByRole in reverse
- Update PR #12400 description to reflect current test counts (56
handler tests, 40 data-layer tests) and safety features
* fix: rollback on rename throw, description validation, delete/DRY cleanup
- Hoist isRename/trimmedName above try block so catch can roll back user
migration when updateRoleByName throws (not just returns null)
- Add description type + max-length (2000) validation in create and update,
consistent with groups handler
- Remove redundant getRoleByName existence check in deleteRoleHandler —
use deleteRoleByName return value directly
- Skip no-op name write when body.name equals current name (use isRename)
- Extract getUserModel() accessor to DRY repeated Model<IUser> casts
- Use name.trim() consistently in createRoleByName error messages
- Add tests: rename-throw rollback, description validation (create+update),
update delete test mocks to match simplified handler
* fix: guard spurious rollback, harden createRole error path, validate before DB calls
- Add migrationRan flag to prevent rollback of user migration that never ran
- Return generic message on 500 in createRoleHandler, specific only for 409
- Move description validation before DB queries in updateRoleHandler
- Return existing role early when update body has no changes
- Wrap cache.set in createRoleByName with try/catch to prevent masking DB success
- Add JSDoc on 11000 catch explaining compound unique index
- Add tests: spurious rollback guard, empty update body, description validation
ordering, listUsersByRole pagination
* fix: validate permissions in create, RoleConflictError, rollback safety, cache consistency
- Add permissions type/array validation in createRoleHandler
- Introduce RoleConflictError class replacing fragile string-prefix matching
- Wrap rollback in !role null path with try/catch for correct 404 response
- Wrap deleteRoleByName cache.set in try/catch matching createRoleByName
- Narrow updateRoleHandler body type to { name?, description? }
- Add tests: non-string description in create, rollback failure logging,
permissions array rejection, description max-length assertion fix
* feat: prevent removing the last admin user
Add guard in removeRoleMember that checks countUsersByRole before
demoting an ADMIN user, returning 400 if they are the last one.
* fix: move interleaved export below imports, add await to countUsersByRole
* fix: paginate listRoles, null-guard permissions handler, fix export ordering
- Add limit/offset/total pagination to listRoles matching the groups pattern
- Add countRoles data-layer method
- Omit permissions from listRoles select (getRole returns full document)
- Null-guard re-fetched role in updateRolePermissionsHandler
- Move interleaved export below all imports in methods/index.ts
* fix: address review findings — race safety, validation DRY, type accuracy, test coverage
- Add post-write admin count verification in removeRoleMember to prevent
zero-admin race condition (TOCTOU → rollback if count hits 0)
- Make IRole.description optional; backfill in initializeRoles for
pre-existing roles that lack the field (.lean() bypasses defaults)
- Extract parsePagination, validateNameParam, validateRoleName, and
validateDescription helpers to eliminate duplicated validation
- Add validateNameParam guard to all 7 handlers reading req.params.name
- Catch 11000 in updateRoleByName and surface as 409 via RoleConflictError
- Add idempotent skip in addRoleMember when user already has target role
- Verify updateRolePermissions test asserts response body
- Add data-layer tests: listRoles sort/pagination/projection, countRoles,
and createRoleByName 11000 duplicate key race
* fix: defensive rollback in removeRoleMember, type/style cleanup, test coverage
- Wrap removeRoleMember post-write admin rollback in try/catch so a
transient DB failure cannot leave the system with zero administrators
- Replace double `as unknown[] as IRole[]` cast with `.lean<IRole[]>()`
- Type parsePagination param explicitly; extract DEFAULT/MAX page constants
- Preserve original error cause in updateRoleByName re-throw
- Add test for rollback failure path in removeRoleMember (returns 400)
- Add test for pre-existing roles missing description field (.lean())
* chore: bump @librechat/data-schemas to 0.0.47
* fix: stale cache on rename, extract renameRole helper, shared pagination, cleanup
- Fix updateRoleByName cache bug: invalidate old key and populate new key
when updates.name differs from roleName (prevents stale cache after rename)
- Extract renameRole helper to eliminate mutable outer-scope state flags
(isRename, trimmedName, migrationRan) in updateRoleHandler
- Unify system-role protection to 403 for both rename-from and rename-to
- Extract parsePagination to shared admin/pagination.ts; use in both
roles.ts and groups.ts
- Extract name.trim() to local const in createRoleByName (was called 5×)
- Remove redundant findOne pre-check in deleteRoleByName
- Replace getUserModel closure with local const declarations
- Remove redundant description ?? '' in createRoleHandler (schema default)
- Add doc comment on updateRolePermissionsHandler noting cache dependency
- Add data-layer tests for cache rename behavior (old key null, new key set)
* fix: harden role guards, add User.role index, validate names, improve tests
- Add index on User.role field for efficient member queries at scale
- Replace fragile SystemRoles key lookup with value-based Set check (6 sites)
- Elevate rename rollback failure logging to CRITICAL (matches removeRoleMember)
- Guard removeRoleMember against non-ADMIN system roles (403 for USER)
- Fix parsePagination limit=0 gotcha: use parseInt + NaN check instead of ||
- Add control character and reserved path segment validation to role names
- Simplify validateRoleName: remove redundant casts and dead conditions
- Add JSDoc to deleteRoleByName documenting non-atomic window
- Split mixed value+type import in methods/index.ts per AGENTS.md
- Add 9 new tests: permissions assertion, combined rename+desc, createRole
with permissions, pagination edge cases, control char/reserved name
rejection, system role removeRoleMember guard
* fix: exact-case reserved name check, consistent validation, cleaner createRole
- Remove .toLowerCase() from reserved name check so only exact matches
(members, permissions) are rejected, not legitimate names like "Members"
- Extract trimmed const in validateRoleName for consistent validation
- Add control char check to validateNameParam for parity with body validation
- Build createRole roleData conditionally to avoid passing description: undefined
- Expand deleteRoleByName JSDoc documenting self-healing design and no-op trade-off
* fix: scope rename rollback to only migrated users, prevent cross-role corruption
Capture user IDs before forward migration so the rollback path only
reverts users this request actually moved. Previously the rollback called
updateUsersByRole(newName, currentName) which would sweep all users with
the new role — including any independently assigned by a concurrent admin
request — causing silent cross-role data corruption.
Adds findUserIdsByRole and updateUsersRoleByIds to the data layer.
Extracts rollbackMigratedUsers helper to deduplicate rollback sites.
* fix: guard last admin in addRoleMember to prevent zero-admin lockout
Since each user has exactly one role, addRoleMember implicitly removes
the user from their current role. Without a guard, reassigning the sole
admin to a non-admin role leaves zero admins and locks out admin
management. Adds the same countUsersByRole check used in removeRoleMember.
* fix: wire findUserIdsByRole and updateUsersRoleByIds into roles route
The scoped rollback deps added in c89b5db were missing from the route
DI wiring, causing renameRole to call undefined and return a 500.
* fix: post-write admin guard in addRoleMember, compound role index, review cleanup
- Add post-write admin count check + rollback to addRoleMember to match
removeRoleMember's two-phase TOCTOU protection (prevents zero-admin via
concurrent requests)
- Replace single-field User.role index with compound { role: 1, tenantId: 1 }
to align with existing multi-tenant index pattern (email, OAuth IDs)
- Narrow listRoles dep return type to RoleListItem (projected fields only)
- Refactor validateDescription to early-return style per AGENTS.md
- Remove redundant double .lean() in updateRoleByName
- Document rename snapshot race window in renameRole JSDoc
- Document cache null-set behavior in deleteRoleByName
- Add routing-coupling comment on RESERVED_ROLE_NAMES
- Add test for addRoleMember post-write rollback
* fix: review cleanup — system-role guard, type safety, JSDoc accuracy, tests
- Add system-role guard to addRoleMember: block direct assignment to
non-ADMIN system roles (403), symmetric with removeRoleMember
- Fix RESERVED_ROLE_NAMES comment: explain semantic URL ambiguity, not
a routing conflict (Express resolves single vs multi-segment correctly)
- Replace _id: unknown with Types.ObjectId | string per AGENTS.md
- Narrow listRoles data-layer return type to Pick<IRole, 'name' | 'description'>
to match the actual .select() projection
- Move updateRoleHandler param check inside try/catch for consistency
- Include user IDs in all CRITICAL rollback failure logs for operator recovery
- Clarify deleteRoleByName JSDoc: replace "self-healing" with "idempotent",
document that recovery requires caller retry
- Add tests: system-role guard, promote non-admin to ADMIN,
findUserIdsByRole throw prevents migration
* fix: include _id in listRoles return type to match RoleListItem
Pick<IRole, 'name' | 'description'> omits _id, making it incompatible
with the handler dep's RoleListItem which requires _id.
* fix: case-insensitive system role guard, reject null permissions, check updateUser result
- System role name checks now use case-insensitive comparison via
toUpperCase() — prevents creating 'admin' or 'user' which would
collide with the legacy roles route that uppercases params
- Reject permissions: null in createRole (typeof null === 'object'
was bypassing the validation)
- Check updateUser return in addRoleMember — return 404 if the user
was deleted between the findUser and updateUser calls
* fix: check updateUser return in removeRoleMember for concurrent delete safety
---------
Co-authored-by: Danny Avila <danny@librechat.ai>
2026-03-27 12:44:47 -07:00
|
|
|
let User: mongoose.Model<IUser>;
|
📦 refactor: Consolidate DB models, encapsulating Mongoose usage in `data-schemas` (#11830)
* chore: move database model methods to /packages/data-schemas
* chore: add TypeScript ESLint rule to warn on unused variables
* refactor: model imports to streamline access
- Consolidated model imports across various files to improve code organization and reduce redundancy.
- Updated imports for models such as Assistant, Message, Conversation, and others to a unified import path.
- Adjusted middleware and service files to reflect the new import structure, ensuring functionality remains intact.
- Enhanced test files to align with the new import paths, maintaining test coverage and integrity.
* chore: migrate database models to packages/data-schemas and refactor all direct Mongoose Model usage outside of data-schemas
* test: update agent model mocks in unit tests
- Added `getAgent` mock to `client.test.js` to enhance test coverage for agent-related functionality.
- Removed redundant `getAgent` and `getAgents` mocks from `openai.spec.js` and `responses.unit.spec.js` to streamline test setup and reduce duplication.
- Ensured consistency in agent mock implementations across test files.
* fix: update types in data-schemas
* refactor: enhance type definitions in transaction and spending methods
- Updated type definitions in `checkBalance.ts` to use specific request and response types.
- Refined `spendTokens.ts` to utilize a new `SpendTxData` interface for better clarity and type safety.
- Improved transaction handling in `transaction.ts` by introducing `TransactionResult` and `TxData` interfaces, ensuring consistent data structures across methods.
- Adjusted unit tests in `transaction.spec.ts` to accommodate new type definitions and enhance robustness.
* refactor: streamline model imports and enhance code organization
- Consolidated model imports across various controllers and services to a unified import path, improving code clarity and reducing redundancy.
- Updated multiple files to reflect the new import structure, ensuring all functionalities remain intact.
- Enhanced overall code organization by removing duplicate import statements and optimizing the usage of model methods.
* feat: implement loadAddedAgent and refactor agent loading logic
- Introduced `loadAddedAgent` function to handle loading agents from added conversations, supporting multi-convo parallel execution.
- Created a new `load.ts` file to encapsulate agent loading functionalities, including `loadEphemeralAgent` and `loadAgent`.
- Updated the `index.ts` file to export the new `load` module instead of the deprecated `loadAgent`.
- Enhanced type definitions and improved error handling in the agent loading process.
- Adjusted unit tests to reflect changes in the agent loading structure and ensure comprehensive coverage.
* refactor: enhance balance handling with new update interface
- Introduced `IBalanceUpdate` interface to streamline balance update operations across the codebase.
- Updated `upsertBalanceFields` method signatures in `balance.ts`, `transaction.ts`, and related tests to utilize the new interface for improved type safety.
- Adjusted type imports in `balance.spec.ts` to include `IBalanceUpdate`, ensuring consistency in balance management functionalities.
- Enhanced overall code clarity and maintainability by refining type definitions related to balance operations.
* feat: add unit tests for loadAgent functionality and enhance agent loading logic
- Introduced comprehensive unit tests for the `loadAgent` function, covering various scenarios including null and empty agent IDs, loading of ephemeral agents, and permission checks.
- Enhanced the `initializeClient` function by moving `getConvoFiles` to the correct position in the database method exports, ensuring proper functionality.
- Improved test coverage for agent loading, including handling of non-existent agents and user permissions.
* chore: reorder memory method exports for consistency
- Moved `deleteAllUserMemories` to the correct position in the exported memory methods, ensuring a consistent and logical order of method exports in `memory.ts`.
2026-02-17 18:23:44 -05:00
|
|
|
let getRoleByName: ReturnType<typeof createRoleMethods>['getRoleByName'];
|
|
|
|
|
let updateAccessPermissions: ReturnType<typeof createRoleMethods>['updateAccessPermissions'];
|
|
|
|
|
let initializeRoles: ReturnType<typeof createRoleMethods>['initializeRoles'];
|
🪪 feat: Admin Roles API Endpoints (#12400)
* feat: add createRole and deleteRole methods to role
* feat: add admin roles handler factory and Express routes
* fix: address convention violations in admin roles handlers
* fix: rename createRole/deleteRole to avoid AccessRole name collision
The existing accessRole.ts already exports createRole/deleteRole for the
AccessRole model. In createMethods index.ts, these are spread after
roleMethods, overwriting them. Renamed our Role methods to
createRoleByName/deleteRoleByName to match the existing pattern
(getRoleByName, updateRoleByName) and avoid the collision.
* feat: add description field to Role model
- Add description to IRole, CreateRoleRequest, UpdateRoleRequest types
- Add description field to Mongoose roleSchema (default: '')
- Wire description through createRoleHandler and updateRoleHandler
- Include description in listRoles select clause so it appears in list
* fix: address Copilot review findings in admin roles handlers
* test: add unit tests for admin roles and groups handlers
* test: add data-layer tests for createRoleByName, deleteRoleByName, listUsersByRole
* fix: allow system role updates when name is unchanged
The updateRoleHandler guard rejected any request where body.name matched
a system role, even when the name was not being changed. This blocked
editing a system role's description. Compare against the URL param to
only reject actual renames to reserved names.
* fix: address external review findings for admin roles
- Block renaming system roles (ADMIN/USER) and add user migration on rename
- Add input validation: name max-length, trim on update, duplicate name check
- Replace fragile String.includes error matching with prefix-based classification
- Catch MongoDB 11000 duplicate key in createRoleByName
- Add pagination (limit/offset/total) to getRoleMembersHandler
- Reverse delete order in deleteRoleByName — reassign users before deletion
- Add role existence check in removeRoleMember; drop unused createdAt select
- Add Array.isArray guard for permissions input; use consistent ?? coalescing
- Fix import ordering per AGENTS.md conventions
- Type-cast mongoose.models.User as Model<IUser> for proper TS inference
- Add comprehensive tests: rename guards, pagination, validation, 500 paths
* fix: address re-review findings for admin roles
- Gate deleteRoleByName on existence check — skip user reassignment and
cache invalidation when role doesn't exist (fixes test mismatch)
- Reverse rename order: migrate users before renaming role so a migration
failure leaves the system in a consistent state
- Add .sort({ _id: 1 }) to listUsersByRole for deterministic pagination
- Import shared AdminMember type from data-schemas instead of local copy;
make joinedAt optional since neither groups nor roles populate it
- Change IRole.description from optional to required to match schema default
- Add data-layer tests for updateUsersByRole and countUsersByRole
- Add handler test verifying users-first rename ordering and migration
failure safety
* fix: add rollback on rename failure and update PR description
- Roll back user migration if updateRoleByName returns null during a
rename (race: role deleted between existence check and update)
- Add test verifying rollback calls updateUsersByRole in reverse
- Update PR #12400 description to reflect current test counts (56
handler tests, 40 data-layer tests) and safety features
* fix: rollback on rename throw, description validation, delete/DRY cleanup
- Hoist isRename/trimmedName above try block so catch can roll back user
migration when updateRoleByName throws (not just returns null)
- Add description type + max-length (2000) validation in create and update,
consistent with groups handler
- Remove redundant getRoleByName existence check in deleteRoleHandler —
use deleteRoleByName return value directly
- Skip no-op name write when body.name equals current name (use isRename)
- Extract getUserModel() accessor to DRY repeated Model<IUser> casts
- Use name.trim() consistently in createRoleByName error messages
- Add tests: rename-throw rollback, description validation (create+update),
update delete test mocks to match simplified handler
* fix: guard spurious rollback, harden createRole error path, validate before DB calls
- Add migrationRan flag to prevent rollback of user migration that never ran
- Return generic message on 500 in createRoleHandler, specific only for 409
- Move description validation before DB queries in updateRoleHandler
- Return existing role early when update body has no changes
- Wrap cache.set in createRoleByName with try/catch to prevent masking DB success
- Add JSDoc on 11000 catch explaining compound unique index
- Add tests: spurious rollback guard, empty update body, description validation
ordering, listUsersByRole pagination
* fix: validate permissions in create, RoleConflictError, rollback safety, cache consistency
- Add permissions type/array validation in createRoleHandler
- Introduce RoleConflictError class replacing fragile string-prefix matching
- Wrap rollback in !role null path with try/catch for correct 404 response
- Wrap deleteRoleByName cache.set in try/catch matching createRoleByName
- Narrow updateRoleHandler body type to { name?, description? }
- Add tests: non-string description in create, rollback failure logging,
permissions array rejection, description max-length assertion fix
* feat: prevent removing the last admin user
Add guard in removeRoleMember that checks countUsersByRole before
demoting an ADMIN user, returning 400 if they are the last one.
* fix: move interleaved export below imports, add await to countUsersByRole
* fix: paginate listRoles, null-guard permissions handler, fix export ordering
- Add limit/offset/total pagination to listRoles matching the groups pattern
- Add countRoles data-layer method
- Omit permissions from listRoles select (getRole returns full document)
- Null-guard re-fetched role in updateRolePermissionsHandler
- Move interleaved export below all imports in methods/index.ts
* fix: address review findings — race safety, validation DRY, type accuracy, test coverage
- Add post-write admin count verification in removeRoleMember to prevent
zero-admin race condition (TOCTOU → rollback if count hits 0)
- Make IRole.description optional; backfill in initializeRoles for
pre-existing roles that lack the field (.lean() bypasses defaults)
- Extract parsePagination, validateNameParam, validateRoleName, and
validateDescription helpers to eliminate duplicated validation
- Add validateNameParam guard to all 7 handlers reading req.params.name
- Catch 11000 in updateRoleByName and surface as 409 via RoleConflictError
- Add idempotent skip in addRoleMember when user already has target role
- Verify updateRolePermissions test asserts response body
- Add data-layer tests: listRoles sort/pagination/projection, countRoles,
and createRoleByName 11000 duplicate key race
* fix: defensive rollback in removeRoleMember, type/style cleanup, test coverage
- Wrap removeRoleMember post-write admin rollback in try/catch so a
transient DB failure cannot leave the system with zero administrators
- Replace double `as unknown[] as IRole[]` cast with `.lean<IRole[]>()`
- Type parsePagination param explicitly; extract DEFAULT/MAX page constants
- Preserve original error cause in updateRoleByName re-throw
- Add test for rollback failure path in removeRoleMember (returns 400)
- Add test for pre-existing roles missing description field (.lean())
* chore: bump @librechat/data-schemas to 0.0.47
* fix: stale cache on rename, extract renameRole helper, shared pagination, cleanup
- Fix updateRoleByName cache bug: invalidate old key and populate new key
when updates.name differs from roleName (prevents stale cache after rename)
- Extract renameRole helper to eliminate mutable outer-scope state flags
(isRename, trimmedName, migrationRan) in updateRoleHandler
- Unify system-role protection to 403 for both rename-from and rename-to
- Extract parsePagination to shared admin/pagination.ts; use in both
roles.ts and groups.ts
- Extract name.trim() to local const in createRoleByName (was called 5×)
- Remove redundant findOne pre-check in deleteRoleByName
- Replace getUserModel closure with local const declarations
- Remove redundant description ?? '' in createRoleHandler (schema default)
- Add doc comment on updateRolePermissionsHandler noting cache dependency
- Add data-layer tests for cache rename behavior (old key null, new key set)
* fix: harden role guards, add User.role index, validate names, improve tests
- Add index on User.role field for efficient member queries at scale
- Replace fragile SystemRoles key lookup with value-based Set check (6 sites)
- Elevate rename rollback failure logging to CRITICAL (matches removeRoleMember)
- Guard removeRoleMember against non-ADMIN system roles (403 for USER)
- Fix parsePagination limit=0 gotcha: use parseInt + NaN check instead of ||
- Add control character and reserved path segment validation to role names
- Simplify validateRoleName: remove redundant casts and dead conditions
- Add JSDoc to deleteRoleByName documenting non-atomic window
- Split mixed value+type import in methods/index.ts per AGENTS.md
- Add 9 new tests: permissions assertion, combined rename+desc, createRole
with permissions, pagination edge cases, control char/reserved name
rejection, system role removeRoleMember guard
* fix: exact-case reserved name check, consistent validation, cleaner createRole
- Remove .toLowerCase() from reserved name check so only exact matches
(members, permissions) are rejected, not legitimate names like "Members"
- Extract trimmed const in validateRoleName for consistent validation
- Add control char check to validateNameParam for parity with body validation
- Build createRole roleData conditionally to avoid passing description: undefined
- Expand deleteRoleByName JSDoc documenting self-healing design and no-op trade-off
* fix: scope rename rollback to only migrated users, prevent cross-role corruption
Capture user IDs before forward migration so the rollback path only
reverts users this request actually moved. Previously the rollback called
updateUsersByRole(newName, currentName) which would sweep all users with
the new role — including any independently assigned by a concurrent admin
request — causing silent cross-role data corruption.
Adds findUserIdsByRole and updateUsersRoleByIds to the data layer.
Extracts rollbackMigratedUsers helper to deduplicate rollback sites.
* fix: guard last admin in addRoleMember to prevent zero-admin lockout
Since each user has exactly one role, addRoleMember implicitly removes
the user from their current role. Without a guard, reassigning the sole
admin to a non-admin role leaves zero admins and locks out admin
management. Adds the same countUsersByRole check used in removeRoleMember.
* fix: wire findUserIdsByRole and updateUsersRoleByIds into roles route
The scoped rollback deps added in c89b5db were missing from the route
DI wiring, causing renameRole to call undefined and return a 500.
* fix: post-write admin guard in addRoleMember, compound role index, review cleanup
- Add post-write admin count check + rollback to addRoleMember to match
removeRoleMember's two-phase TOCTOU protection (prevents zero-admin via
concurrent requests)
- Replace single-field User.role index with compound { role: 1, tenantId: 1 }
to align with existing multi-tenant index pattern (email, OAuth IDs)
- Narrow listRoles dep return type to RoleListItem (projected fields only)
- Refactor validateDescription to early-return style per AGENTS.md
- Remove redundant double .lean() in updateRoleByName
- Document rename snapshot race window in renameRole JSDoc
- Document cache null-set behavior in deleteRoleByName
- Add routing-coupling comment on RESERVED_ROLE_NAMES
- Add test for addRoleMember post-write rollback
* fix: review cleanup — system-role guard, type safety, JSDoc accuracy, tests
- Add system-role guard to addRoleMember: block direct assignment to
non-ADMIN system roles (403), symmetric with removeRoleMember
- Fix RESERVED_ROLE_NAMES comment: explain semantic URL ambiguity, not
a routing conflict (Express resolves single vs multi-segment correctly)
- Replace _id: unknown with Types.ObjectId | string per AGENTS.md
- Narrow listRoles data-layer return type to Pick<IRole, 'name' | 'description'>
to match the actual .select() projection
- Move updateRoleHandler param check inside try/catch for consistency
- Include user IDs in all CRITICAL rollback failure logs for operator recovery
- Clarify deleteRoleByName JSDoc: replace "self-healing" with "idempotent",
document that recovery requires caller retry
- Add tests: system-role guard, promote non-admin to ADMIN,
findUserIdsByRole throw prevents migration
* fix: include _id in listRoles return type to match RoleListItem
Pick<IRole, 'name' | 'description'> omits _id, making it incompatible
with the handler dep's RoleListItem which requires _id.
* fix: case-insensitive system role guard, reject null permissions, check updateUser result
- System role name checks now use case-insensitive comparison via
toUpperCase() — prevents creating 'admin' or 'user' which would
collide with the legacy roles route that uppercases params
- Reject permissions: null in createRole (typeof null === 'object'
was bypassing the validation)
- Check updateUser return in addRoleMember — return 404 if the user
was deleted between the findUser and updateUser calls
* fix: check updateUser return in removeRoleMember for concurrent delete safety
---------
Co-authored-by: Danny Avila <danny@librechat.ai>
2026-03-27 12:44:47 -07:00
|
|
|
let createRoleByName: ReturnType<typeof createRoleMethods>['createRoleByName'];
|
|
|
|
|
let deleteRoleByName: ReturnType<typeof createRoleMethods>['deleteRoleByName'];
|
|
|
|
|
let updateUsersByRole: ReturnType<typeof createRoleMethods>['updateUsersByRole'];
|
|
|
|
|
let listUsersByRole: ReturnType<typeof createRoleMethods>['listUsersByRole'];
|
|
|
|
|
let countUsersByRole: ReturnType<typeof createRoleMethods>['countUsersByRole'];
|
|
|
|
|
let updateRoleByName: ReturnType<typeof createRoleMethods>['updateRoleByName'];
|
|
|
|
|
let listRoles: ReturnType<typeof createRoleMethods>['listRoles'];
|
|
|
|
|
let countRoles: ReturnType<typeof createRoleMethods>['countRoles'];
|
📦 refactor: Consolidate DB models, encapsulating Mongoose usage in `data-schemas` (#11830)
* chore: move database model methods to /packages/data-schemas
* chore: add TypeScript ESLint rule to warn on unused variables
* refactor: model imports to streamline access
- Consolidated model imports across various files to improve code organization and reduce redundancy.
- Updated imports for models such as Assistant, Message, Conversation, and others to a unified import path.
- Adjusted middleware and service files to reflect the new import structure, ensuring functionality remains intact.
- Enhanced test files to align with the new import paths, maintaining test coverage and integrity.
* chore: migrate database models to packages/data-schemas and refactor all direct Mongoose Model usage outside of data-schemas
* test: update agent model mocks in unit tests
- Added `getAgent` mock to `client.test.js` to enhance test coverage for agent-related functionality.
- Removed redundant `getAgent` and `getAgents` mocks from `openai.spec.js` and `responses.unit.spec.js` to streamline test setup and reduce duplication.
- Ensured consistency in agent mock implementations across test files.
* fix: update types in data-schemas
* refactor: enhance type definitions in transaction and spending methods
- Updated type definitions in `checkBalance.ts` to use specific request and response types.
- Refined `spendTokens.ts` to utilize a new `SpendTxData` interface for better clarity and type safety.
- Improved transaction handling in `transaction.ts` by introducing `TransactionResult` and `TxData` interfaces, ensuring consistent data structures across methods.
- Adjusted unit tests in `transaction.spec.ts` to accommodate new type definitions and enhance robustness.
* refactor: streamline model imports and enhance code organization
- Consolidated model imports across various controllers and services to a unified import path, improving code clarity and reducing redundancy.
- Updated multiple files to reflect the new import structure, ensuring all functionalities remain intact.
- Enhanced overall code organization by removing duplicate import statements and optimizing the usage of model methods.
* feat: implement loadAddedAgent and refactor agent loading logic
- Introduced `loadAddedAgent` function to handle loading agents from added conversations, supporting multi-convo parallel execution.
- Created a new `load.ts` file to encapsulate agent loading functionalities, including `loadEphemeralAgent` and `loadAgent`.
- Updated the `index.ts` file to export the new `load` module instead of the deprecated `loadAgent`.
- Enhanced type definitions and improved error handling in the agent loading process.
- Adjusted unit tests to reflect changes in the agent loading structure and ensure comprehensive coverage.
* refactor: enhance balance handling with new update interface
- Introduced `IBalanceUpdate` interface to streamline balance update operations across the codebase.
- Updated `upsertBalanceFields` method signatures in `balance.ts`, `transaction.ts`, and related tests to utilize the new interface for improved type safety.
- Adjusted type imports in `balance.spec.ts` to include `IBalanceUpdate`, ensuring consistency in balance management functionalities.
- Enhanced overall code clarity and maintainability by refining type definitions related to balance operations.
* feat: add unit tests for loadAgent functionality and enhance agent loading logic
- Introduced comprehensive unit tests for the `loadAgent` function, covering various scenarios including null and empty agent IDs, loading of ephemeral agents, and permission checks.
- Enhanced the `initializeClient` function by moving `getConvoFiles` to the correct position in the database method exports, ensuring proper functionality.
- Improved test coverage for agent loading, including handling of non-existent agents and user permissions.
* chore: reorder memory method exports for consistency
- Moved `deleteAllUserMemories` to the correct position in the exported memory methods, ensuring a consistent and logical order of method exports in `memory.ts`.
2026-02-17 18:23:44 -05:00
|
|
|
let mongoServer: MongoMemoryServer;
|
2024-08-26 15:34:46 -04:00
|
|
|
|
|
|
|
|
beforeAll(async () => {
|
|
|
|
|
mongoServer = await MongoMemoryServer.create();
|
|
|
|
|
const mongoUri = mongoServer.getUri();
|
|
|
|
|
await mongoose.connect(mongoUri);
|
📦 refactor: Consolidate DB models, encapsulating Mongoose usage in `data-schemas` (#11830)
* chore: move database model methods to /packages/data-schemas
* chore: add TypeScript ESLint rule to warn on unused variables
* refactor: model imports to streamline access
- Consolidated model imports across various files to improve code organization and reduce redundancy.
- Updated imports for models such as Assistant, Message, Conversation, and others to a unified import path.
- Adjusted middleware and service files to reflect the new import structure, ensuring functionality remains intact.
- Enhanced test files to align with the new import paths, maintaining test coverage and integrity.
* chore: migrate database models to packages/data-schemas and refactor all direct Mongoose Model usage outside of data-schemas
* test: update agent model mocks in unit tests
- Added `getAgent` mock to `client.test.js` to enhance test coverage for agent-related functionality.
- Removed redundant `getAgent` and `getAgents` mocks from `openai.spec.js` and `responses.unit.spec.js` to streamline test setup and reduce duplication.
- Ensured consistency in agent mock implementations across test files.
* fix: update types in data-schemas
* refactor: enhance type definitions in transaction and spending methods
- Updated type definitions in `checkBalance.ts` to use specific request and response types.
- Refined `spendTokens.ts` to utilize a new `SpendTxData` interface for better clarity and type safety.
- Improved transaction handling in `transaction.ts` by introducing `TransactionResult` and `TxData` interfaces, ensuring consistent data structures across methods.
- Adjusted unit tests in `transaction.spec.ts` to accommodate new type definitions and enhance robustness.
* refactor: streamline model imports and enhance code organization
- Consolidated model imports across various controllers and services to a unified import path, improving code clarity and reducing redundancy.
- Updated multiple files to reflect the new import structure, ensuring all functionalities remain intact.
- Enhanced overall code organization by removing duplicate import statements and optimizing the usage of model methods.
* feat: implement loadAddedAgent and refactor agent loading logic
- Introduced `loadAddedAgent` function to handle loading agents from added conversations, supporting multi-convo parallel execution.
- Created a new `load.ts` file to encapsulate agent loading functionalities, including `loadEphemeralAgent` and `loadAgent`.
- Updated the `index.ts` file to export the new `load` module instead of the deprecated `loadAgent`.
- Enhanced type definitions and improved error handling in the agent loading process.
- Adjusted unit tests to reflect changes in the agent loading structure and ensure comprehensive coverage.
* refactor: enhance balance handling with new update interface
- Introduced `IBalanceUpdate` interface to streamline balance update operations across the codebase.
- Updated `upsertBalanceFields` method signatures in `balance.ts`, `transaction.ts`, and related tests to utilize the new interface for improved type safety.
- Adjusted type imports in `balance.spec.ts` to include `IBalanceUpdate`, ensuring consistency in balance management functionalities.
- Enhanced overall code clarity and maintainability by refining type definitions related to balance operations.
* feat: add unit tests for loadAgent functionality and enhance agent loading logic
- Introduced comprehensive unit tests for the `loadAgent` function, covering various scenarios including null and empty agent IDs, loading of ephemeral agents, and permission checks.
- Enhanced the `initializeClient` function by moving `getConvoFiles` to the correct position in the database method exports, ensuring proper functionality.
- Improved test coverage for agent loading, including handling of non-existent agents and user permissions.
* chore: reorder memory method exports for consistency
- Moved `deleteAllUserMemories` to the correct position in the exported memory methods, ensuring a consistent and logical order of method exports in `memory.ts`.
2026-02-17 18:23:44 -05:00
|
|
|
createModels(mongoose);
|
|
|
|
|
Role = mongoose.models.Role;
|
🪪 feat: Admin Roles API Endpoints (#12400)
* feat: add createRole and deleteRole methods to role
* feat: add admin roles handler factory and Express routes
* fix: address convention violations in admin roles handlers
* fix: rename createRole/deleteRole to avoid AccessRole name collision
The existing accessRole.ts already exports createRole/deleteRole for the
AccessRole model. In createMethods index.ts, these are spread after
roleMethods, overwriting them. Renamed our Role methods to
createRoleByName/deleteRoleByName to match the existing pattern
(getRoleByName, updateRoleByName) and avoid the collision.
* feat: add description field to Role model
- Add description to IRole, CreateRoleRequest, UpdateRoleRequest types
- Add description field to Mongoose roleSchema (default: '')
- Wire description through createRoleHandler and updateRoleHandler
- Include description in listRoles select clause so it appears in list
* fix: address Copilot review findings in admin roles handlers
* test: add unit tests for admin roles and groups handlers
* test: add data-layer tests for createRoleByName, deleteRoleByName, listUsersByRole
* fix: allow system role updates when name is unchanged
The updateRoleHandler guard rejected any request where body.name matched
a system role, even when the name was not being changed. This blocked
editing a system role's description. Compare against the URL param to
only reject actual renames to reserved names.
* fix: address external review findings for admin roles
- Block renaming system roles (ADMIN/USER) and add user migration on rename
- Add input validation: name max-length, trim on update, duplicate name check
- Replace fragile String.includes error matching with prefix-based classification
- Catch MongoDB 11000 duplicate key in createRoleByName
- Add pagination (limit/offset/total) to getRoleMembersHandler
- Reverse delete order in deleteRoleByName — reassign users before deletion
- Add role existence check in removeRoleMember; drop unused createdAt select
- Add Array.isArray guard for permissions input; use consistent ?? coalescing
- Fix import ordering per AGENTS.md conventions
- Type-cast mongoose.models.User as Model<IUser> for proper TS inference
- Add comprehensive tests: rename guards, pagination, validation, 500 paths
* fix: address re-review findings for admin roles
- Gate deleteRoleByName on existence check — skip user reassignment and
cache invalidation when role doesn't exist (fixes test mismatch)
- Reverse rename order: migrate users before renaming role so a migration
failure leaves the system in a consistent state
- Add .sort({ _id: 1 }) to listUsersByRole for deterministic pagination
- Import shared AdminMember type from data-schemas instead of local copy;
make joinedAt optional since neither groups nor roles populate it
- Change IRole.description from optional to required to match schema default
- Add data-layer tests for updateUsersByRole and countUsersByRole
- Add handler test verifying users-first rename ordering and migration
failure safety
* fix: add rollback on rename failure and update PR description
- Roll back user migration if updateRoleByName returns null during a
rename (race: role deleted between existence check and update)
- Add test verifying rollback calls updateUsersByRole in reverse
- Update PR #12400 description to reflect current test counts (56
handler tests, 40 data-layer tests) and safety features
* fix: rollback on rename throw, description validation, delete/DRY cleanup
- Hoist isRename/trimmedName above try block so catch can roll back user
migration when updateRoleByName throws (not just returns null)
- Add description type + max-length (2000) validation in create and update,
consistent with groups handler
- Remove redundant getRoleByName existence check in deleteRoleHandler —
use deleteRoleByName return value directly
- Skip no-op name write when body.name equals current name (use isRename)
- Extract getUserModel() accessor to DRY repeated Model<IUser> casts
- Use name.trim() consistently in createRoleByName error messages
- Add tests: rename-throw rollback, description validation (create+update),
update delete test mocks to match simplified handler
* fix: guard spurious rollback, harden createRole error path, validate before DB calls
- Add migrationRan flag to prevent rollback of user migration that never ran
- Return generic message on 500 in createRoleHandler, specific only for 409
- Move description validation before DB queries in updateRoleHandler
- Return existing role early when update body has no changes
- Wrap cache.set in createRoleByName with try/catch to prevent masking DB success
- Add JSDoc on 11000 catch explaining compound unique index
- Add tests: spurious rollback guard, empty update body, description validation
ordering, listUsersByRole pagination
* fix: validate permissions in create, RoleConflictError, rollback safety, cache consistency
- Add permissions type/array validation in createRoleHandler
- Introduce RoleConflictError class replacing fragile string-prefix matching
- Wrap rollback in !role null path with try/catch for correct 404 response
- Wrap deleteRoleByName cache.set in try/catch matching createRoleByName
- Narrow updateRoleHandler body type to { name?, description? }
- Add tests: non-string description in create, rollback failure logging,
permissions array rejection, description max-length assertion fix
* feat: prevent removing the last admin user
Add guard in removeRoleMember that checks countUsersByRole before
demoting an ADMIN user, returning 400 if they are the last one.
* fix: move interleaved export below imports, add await to countUsersByRole
* fix: paginate listRoles, null-guard permissions handler, fix export ordering
- Add limit/offset/total pagination to listRoles matching the groups pattern
- Add countRoles data-layer method
- Omit permissions from listRoles select (getRole returns full document)
- Null-guard re-fetched role in updateRolePermissionsHandler
- Move interleaved export below all imports in methods/index.ts
* fix: address review findings — race safety, validation DRY, type accuracy, test coverage
- Add post-write admin count verification in removeRoleMember to prevent
zero-admin race condition (TOCTOU → rollback if count hits 0)
- Make IRole.description optional; backfill in initializeRoles for
pre-existing roles that lack the field (.lean() bypasses defaults)
- Extract parsePagination, validateNameParam, validateRoleName, and
validateDescription helpers to eliminate duplicated validation
- Add validateNameParam guard to all 7 handlers reading req.params.name
- Catch 11000 in updateRoleByName and surface as 409 via RoleConflictError
- Add idempotent skip in addRoleMember when user already has target role
- Verify updateRolePermissions test asserts response body
- Add data-layer tests: listRoles sort/pagination/projection, countRoles,
and createRoleByName 11000 duplicate key race
* fix: defensive rollback in removeRoleMember, type/style cleanup, test coverage
- Wrap removeRoleMember post-write admin rollback in try/catch so a
transient DB failure cannot leave the system with zero administrators
- Replace double `as unknown[] as IRole[]` cast with `.lean<IRole[]>()`
- Type parsePagination param explicitly; extract DEFAULT/MAX page constants
- Preserve original error cause in updateRoleByName re-throw
- Add test for rollback failure path in removeRoleMember (returns 400)
- Add test for pre-existing roles missing description field (.lean())
* chore: bump @librechat/data-schemas to 0.0.47
* fix: stale cache on rename, extract renameRole helper, shared pagination, cleanup
- Fix updateRoleByName cache bug: invalidate old key and populate new key
when updates.name differs from roleName (prevents stale cache after rename)
- Extract renameRole helper to eliminate mutable outer-scope state flags
(isRename, trimmedName, migrationRan) in updateRoleHandler
- Unify system-role protection to 403 for both rename-from and rename-to
- Extract parsePagination to shared admin/pagination.ts; use in both
roles.ts and groups.ts
- Extract name.trim() to local const in createRoleByName (was called 5×)
- Remove redundant findOne pre-check in deleteRoleByName
- Replace getUserModel closure with local const declarations
- Remove redundant description ?? '' in createRoleHandler (schema default)
- Add doc comment on updateRolePermissionsHandler noting cache dependency
- Add data-layer tests for cache rename behavior (old key null, new key set)
* fix: harden role guards, add User.role index, validate names, improve tests
- Add index on User.role field for efficient member queries at scale
- Replace fragile SystemRoles key lookup with value-based Set check (6 sites)
- Elevate rename rollback failure logging to CRITICAL (matches removeRoleMember)
- Guard removeRoleMember against non-ADMIN system roles (403 for USER)
- Fix parsePagination limit=0 gotcha: use parseInt + NaN check instead of ||
- Add control character and reserved path segment validation to role names
- Simplify validateRoleName: remove redundant casts and dead conditions
- Add JSDoc to deleteRoleByName documenting non-atomic window
- Split mixed value+type import in methods/index.ts per AGENTS.md
- Add 9 new tests: permissions assertion, combined rename+desc, createRole
with permissions, pagination edge cases, control char/reserved name
rejection, system role removeRoleMember guard
* fix: exact-case reserved name check, consistent validation, cleaner createRole
- Remove .toLowerCase() from reserved name check so only exact matches
(members, permissions) are rejected, not legitimate names like "Members"
- Extract trimmed const in validateRoleName for consistent validation
- Add control char check to validateNameParam for parity with body validation
- Build createRole roleData conditionally to avoid passing description: undefined
- Expand deleteRoleByName JSDoc documenting self-healing design and no-op trade-off
* fix: scope rename rollback to only migrated users, prevent cross-role corruption
Capture user IDs before forward migration so the rollback path only
reverts users this request actually moved. Previously the rollback called
updateUsersByRole(newName, currentName) which would sweep all users with
the new role — including any independently assigned by a concurrent admin
request — causing silent cross-role data corruption.
Adds findUserIdsByRole and updateUsersRoleByIds to the data layer.
Extracts rollbackMigratedUsers helper to deduplicate rollback sites.
* fix: guard last admin in addRoleMember to prevent zero-admin lockout
Since each user has exactly one role, addRoleMember implicitly removes
the user from their current role. Without a guard, reassigning the sole
admin to a non-admin role leaves zero admins and locks out admin
management. Adds the same countUsersByRole check used in removeRoleMember.
* fix: wire findUserIdsByRole and updateUsersRoleByIds into roles route
The scoped rollback deps added in c89b5db were missing from the route
DI wiring, causing renameRole to call undefined and return a 500.
* fix: post-write admin guard in addRoleMember, compound role index, review cleanup
- Add post-write admin count check + rollback to addRoleMember to match
removeRoleMember's two-phase TOCTOU protection (prevents zero-admin via
concurrent requests)
- Replace single-field User.role index with compound { role: 1, tenantId: 1 }
to align with existing multi-tenant index pattern (email, OAuth IDs)
- Narrow listRoles dep return type to RoleListItem (projected fields only)
- Refactor validateDescription to early-return style per AGENTS.md
- Remove redundant double .lean() in updateRoleByName
- Document rename snapshot race window in renameRole JSDoc
- Document cache null-set behavior in deleteRoleByName
- Add routing-coupling comment on RESERVED_ROLE_NAMES
- Add test for addRoleMember post-write rollback
* fix: review cleanup — system-role guard, type safety, JSDoc accuracy, tests
- Add system-role guard to addRoleMember: block direct assignment to
non-ADMIN system roles (403), symmetric with removeRoleMember
- Fix RESERVED_ROLE_NAMES comment: explain semantic URL ambiguity, not
a routing conflict (Express resolves single vs multi-segment correctly)
- Replace _id: unknown with Types.ObjectId | string per AGENTS.md
- Narrow listRoles data-layer return type to Pick<IRole, 'name' | 'description'>
to match the actual .select() projection
- Move updateRoleHandler param check inside try/catch for consistency
- Include user IDs in all CRITICAL rollback failure logs for operator recovery
- Clarify deleteRoleByName JSDoc: replace "self-healing" with "idempotent",
document that recovery requires caller retry
- Add tests: system-role guard, promote non-admin to ADMIN,
findUserIdsByRole throw prevents migration
* fix: include _id in listRoles return type to match RoleListItem
Pick<IRole, 'name' | 'description'> omits _id, making it incompatible
with the handler dep's RoleListItem which requires _id.
* fix: case-insensitive system role guard, reject null permissions, check updateUser result
- System role name checks now use case-insensitive comparison via
toUpperCase() — prevents creating 'admin' or 'user' which would
collide with the legacy roles route that uppercases params
- Reject permissions: null in createRole (typeof null === 'object'
was bypassing the validation)
- Check updateUser return in addRoleMember — return 404 if the user
was deleted between the findUser and updateUser calls
* fix: check updateUser return in removeRoleMember for concurrent delete safety
---------
Co-authored-by: Danny Avila <danny@librechat.ai>
2026-03-27 12:44:47 -07:00
|
|
|
User = mongoose.models.User as mongoose.Model<IUser>;
|
📦 refactor: Consolidate DB models, encapsulating Mongoose usage in `data-schemas` (#11830)
* chore: move database model methods to /packages/data-schemas
* chore: add TypeScript ESLint rule to warn on unused variables
* refactor: model imports to streamline access
- Consolidated model imports across various files to improve code organization and reduce redundancy.
- Updated imports for models such as Assistant, Message, Conversation, and others to a unified import path.
- Adjusted middleware and service files to reflect the new import structure, ensuring functionality remains intact.
- Enhanced test files to align with the new import paths, maintaining test coverage and integrity.
* chore: migrate database models to packages/data-schemas and refactor all direct Mongoose Model usage outside of data-schemas
* test: update agent model mocks in unit tests
- Added `getAgent` mock to `client.test.js` to enhance test coverage for agent-related functionality.
- Removed redundant `getAgent` and `getAgents` mocks from `openai.spec.js` and `responses.unit.spec.js` to streamline test setup and reduce duplication.
- Ensured consistency in agent mock implementations across test files.
* fix: update types in data-schemas
* refactor: enhance type definitions in transaction and spending methods
- Updated type definitions in `checkBalance.ts` to use specific request and response types.
- Refined `spendTokens.ts` to utilize a new `SpendTxData` interface for better clarity and type safety.
- Improved transaction handling in `transaction.ts` by introducing `TransactionResult` and `TxData` interfaces, ensuring consistent data structures across methods.
- Adjusted unit tests in `transaction.spec.ts` to accommodate new type definitions and enhance robustness.
* refactor: streamline model imports and enhance code organization
- Consolidated model imports across various controllers and services to a unified import path, improving code clarity and reducing redundancy.
- Updated multiple files to reflect the new import structure, ensuring all functionalities remain intact.
- Enhanced overall code organization by removing duplicate import statements and optimizing the usage of model methods.
* feat: implement loadAddedAgent and refactor agent loading logic
- Introduced `loadAddedAgent` function to handle loading agents from added conversations, supporting multi-convo parallel execution.
- Created a new `load.ts` file to encapsulate agent loading functionalities, including `loadEphemeralAgent` and `loadAgent`.
- Updated the `index.ts` file to export the new `load` module instead of the deprecated `loadAgent`.
- Enhanced type definitions and improved error handling in the agent loading process.
- Adjusted unit tests to reflect changes in the agent loading structure and ensure comprehensive coverage.
* refactor: enhance balance handling with new update interface
- Introduced `IBalanceUpdate` interface to streamline balance update operations across the codebase.
- Updated `upsertBalanceFields` method signatures in `balance.ts`, `transaction.ts`, and related tests to utilize the new interface for improved type safety.
- Adjusted type imports in `balance.spec.ts` to include `IBalanceUpdate`, ensuring consistency in balance management functionalities.
- Enhanced overall code clarity and maintainability by refining type definitions related to balance operations.
* feat: add unit tests for loadAgent functionality and enhance agent loading logic
- Introduced comprehensive unit tests for the `loadAgent` function, covering various scenarios including null and empty agent IDs, loading of ephemeral agents, and permission checks.
- Enhanced the `initializeClient` function by moving `getConvoFiles` to the correct position in the database method exports, ensuring proper functionality.
- Improved test coverage for agent loading, including handling of non-existent agents and user permissions.
* chore: reorder memory method exports for consistency
- Moved `deleteAllUserMemories` to the correct position in the exported memory methods, ensuring a consistent and logical order of method exports in `memory.ts`.
2026-02-17 18:23:44 -05:00
|
|
|
const methods = createRoleMethods(mongoose, { getCache: mockGetCache });
|
|
|
|
|
getRoleByName = methods.getRoleByName;
|
|
|
|
|
updateAccessPermissions = methods.updateAccessPermissions;
|
|
|
|
|
initializeRoles = methods.initializeRoles;
|
🪪 feat: Admin Roles API Endpoints (#12400)
* feat: add createRole and deleteRole methods to role
* feat: add admin roles handler factory and Express routes
* fix: address convention violations in admin roles handlers
* fix: rename createRole/deleteRole to avoid AccessRole name collision
The existing accessRole.ts already exports createRole/deleteRole for the
AccessRole model. In createMethods index.ts, these are spread after
roleMethods, overwriting them. Renamed our Role methods to
createRoleByName/deleteRoleByName to match the existing pattern
(getRoleByName, updateRoleByName) and avoid the collision.
* feat: add description field to Role model
- Add description to IRole, CreateRoleRequest, UpdateRoleRequest types
- Add description field to Mongoose roleSchema (default: '')
- Wire description through createRoleHandler and updateRoleHandler
- Include description in listRoles select clause so it appears in list
* fix: address Copilot review findings in admin roles handlers
* test: add unit tests for admin roles and groups handlers
* test: add data-layer tests for createRoleByName, deleteRoleByName, listUsersByRole
* fix: allow system role updates when name is unchanged
The updateRoleHandler guard rejected any request where body.name matched
a system role, even when the name was not being changed. This blocked
editing a system role's description. Compare against the URL param to
only reject actual renames to reserved names.
* fix: address external review findings for admin roles
- Block renaming system roles (ADMIN/USER) and add user migration on rename
- Add input validation: name max-length, trim on update, duplicate name check
- Replace fragile String.includes error matching with prefix-based classification
- Catch MongoDB 11000 duplicate key in createRoleByName
- Add pagination (limit/offset/total) to getRoleMembersHandler
- Reverse delete order in deleteRoleByName — reassign users before deletion
- Add role existence check in removeRoleMember; drop unused createdAt select
- Add Array.isArray guard for permissions input; use consistent ?? coalescing
- Fix import ordering per AGENTS.md conventions
- Type-cast mongoose.models.User as Model<IUser> for proper TS inference
- Add comprehensive tests: rename guards, pagination, validation, 500 paths
* fix: address re-review findings for admin roles
- Gate deleteRoleByName on existence check — skip user reassignment and
cache invalidation when role doesn't exist (fixes test mismatch)
- Reverse rename order: migrate users before renaming role so a migration
failure leaves the system in a consistent state
- Add .sort({ _id: 1 }) to listUsersByRole for deterministic pagination
- Import shared AdminMember type from data-schemas instead of local copy;
make joinedAt optional since neither groups nor roles populate it
- Change IRole.description from optional to required to match schema default
- Add data-layer tests for updateUsersByRole and countUsersByRole
- Add handler test verifying users-first rename ordering and migration
failure safety
* fix: add rollback on rename failure and update PR description
- Roll back user migration if updateRoleByName returns null during a
rename (race: role deleted between existence check and update)
- Add test verifying rollback calls updateUsersByRole in reverse
- Update PR #12400 description to reflect current test counts (56
handler tests, 40 data-layer tests) and safety features
* fix: rollback on rename throw, description validation, delete/DRY cleanup
- Hoist isRename/trimmedName above try block so catch can roll back user
migration when updateRoleByName throws (not just returns null)
- Add description type + max-length (2000) validation in create and update,
consistent with groups handler
- Remove redundant getRoleByName existence check in deleteRoleHandler —
use deleteRoleByName return value directly
- Skip no-op name write when body.name equals current name (use isRename)
- Extract getUserModel() accessor to DRY repeated Model<IUser> casts
- Use name.trim() consistently in createRoleByName error messages
- Add tests: rename-throw rollback, description validation (create+update),
update delete test mocks to match simplified handler
* fix: guard spurious rollback, harden createRole error path, validate before DB calls
- Add migrationRan flag to prevent rollback of user migration that never ran
- Return generic message on 500 in createRoleHandler, specific only for 409
- Move description validation before DB queries in updateRoleHandler
- Return existing role early when update body has no changes
- Wrap cache.set in createRoleByName with try/catch to prevent masking DB success
- Add JSDoc on 11000 catch explaining compound unique index
- Add tests: spurious rollback guard, empty update body, description validation
ordering, listUsersByRole pagination
* fix: validate permissions in create, RoleConflictError, rollback safety, cache consistency
- Add permissions type/array validation in createRoleHandler
- Introduce RoleConflictError class replacing fragile string-prefix matching
- Wrap rollback in !role null path with try/catch for correct 404 response
- Wrap deleteRoleByName cache.set in try/catch matching createRoleByName
- Narrow updateRoleHandler body type to { name?, description? }
- Add tests: non-string description in create, rollback failure logging,
permissions array rejection, description max-length assertion fix
* feat: prevent removing the last admin user
Add guard in removeRoleMember that checks countUsersByRole before
demoting an ADMIN user, returning 400 if they are the last one.
* fix: move interleaved export below imports, add await to countUsersByRole
* fix: paginate listRoles, null-guard permissions handler, fix export ordering
- Add limit/offset/total pagination to listRoles matching the groups pattern
- Add countRoles data-layer method
- Omit permissions from listRoles select (getRole returns full document)
- Null-guard re-fetched role in updateRolePermissionsHandler
- Move interleaved export below all imports in methods/index.ts
* fix: address review findings — race safety, validation DRY, type accuracy, test coverage
- Add post-write admin count verification in removeRoleMember to prevent
zero-admin race condition (TOCTOU → rollback if count hits 0)
- Make IRole.description optional; backfill in initializeRoles for
pre-existing roles that lack the field (.lean() bypasses defaults)
- Extract parsePagination, validateNameParam, validateRoleName, and
validateDescription helpers to eliminate duplicated validation
- Add validateNameParam guard to all 7 handlers reading req.params.name
- Catch 11000 in updateRoleByName and surface as 409 via RoleConflictError
- Add idempotent skip in addRoleMember when user already has target role
- Verify updateRolePermissions test asserts response body
- Add data-layer tests: listRoles sort/pagination/projection, countRoles,
and createRoleByName 11000 duplicate key race
* fix: defensive rollback in removeRoleMember, type/style cleanup, test coverage
- Wrap removeRoleMember post-write admin rollback in try/catch so a
transient DB failure cannot leave the system with zero administrators
- Replace double `as unknown[] as IRole[]` cast with `.lean<IRole[]>()`
- Type parsePagination param explicitly; extract DEFAULT/MAX page constants
- Preserve original error cause in updateRoleByName re-throw
- Add test for rollback failure path in removeRoleMember (returns 400)
- Add test for pre-existing roles missing description field (.lean())
* chore: bump @librechat/data-schemas to 0.0.47
* fix: stale cache on rename, extract renameRole helper, shared pagination, cleanup
- Fix updateRoleByName cache bug: invalidate old key and populate new key
when updates.name differs from roleName (prevents stale cache after rename)
- Extract renameRole helper to eliminate mutable outer-scope state flags
(isRename, trimmedName, migrationRan) in updateRoleHandler
- Unify system-role protection to 403 for both rename-from and rename-to
- Extract parsePagination to shared admin/pagination.ts; use in both
roles.ts and groups.ts
- Extract name.trim() to local const in createRoleByName (was called 5×)
- Remove redundant findOne pre-check in deleteRoleByName
- Replace getUserModel closure with local const declarations
- Remove redundant description ?? '' in createRoleHandler (schema default)
- Add doc comment on updateRolePermissionsHandler noting cache dependency
- Add data-layer tests for cache rename behavior (old key null, new key set)
* fix: harden role guards, add User.role index, validate names, improve tests
- Add index on User.role field for efficient member queries at scale
- Replace fragile SystemRoles key lookup with value-based Set check (6 sites)
- Elevate rename rollback failure logging to CRITICAL (matches removeRoleMember)
- Guard removeRoleMember against non-ADMIN system roles (403 for USER)
- Fix parsePagination limit=0 gotcha: use parseInt + NaN check instead of ||
- Add control character and reserved path segment validation to role names
- Simplify validateRoleName: remove redundant casts and dead conditions
- Add JSDoc to deleteRoleByName documenting non-atomic window
- Split mixed value+type import in methods/index.ts per AGENTS.md
- Add 9 new tests: permissions assertion, combined rename+desc, createRole
with permissions, pagination edge cases, control char/reserved name
rejection, system role removeRoleMember guard
* fix: exact-case reserved name check, consistent validation, cleaner createRole
- Remove .toLowerCase() from reserved name check so only exact matches
(members, permissions) are rejected, not legitimate names like "Members"
- Extract trimmed const in validateRoleName for consistent validation
- Add control char check to validateNameParam for parity with body validation
- Build createRole roleData conditionally to avoid passing description: undefined
- Expand deleteRoleByName JSDoc documenting self-healing design and no-op trade-off
* fix: scope rename rollback to only migrated users, prevent cross-role corruption
Capture user IDs before forward migration so the rollback path only
reverts users this request actually moved. Previously the rollback called
updateUsersByRole(newName, currentName) which would sweep all users with
the new role — including any independently assigned by a concurrent admin
request — causing silent cross-role data corruption.
Adds findUserIdsByRole and updateUsersRoleByIds to the data layer.
Extracts rollbackMigratedUsers helper to deduplicate rollback sites.
* fix: guard last admin in addRoleMember to prevent zero-admin lockout
Since each user has exactly one role, addRoleMember implicitly removes
the user from their current role. Without a guard, reassigning the sole
admin to a non-admin role leaves zero admins and locks out admin
management. Adds the same countUsersByRole check used in removeRoleMember.
* fix: wire findUserIdsByRole and updateUsersRoleByIds into roles route
The scoped rollback deps added in c89b5db were missing from the route
DI wiring, causing renameRole to call undefined and return a 500.
* fix: post-write admin guard in addRoleMember, compound role index, review cleanup
- Add post-write admin count check + rollback to addRoleMember to match
removeRoleMember's two-phase TOCTOU protection (prevents zero-admin via
concurrent requests)
- Replace single-field User.role index with compound { role: 1, tenantId: 1 }
to align with existing multi-tenant index pattern (email, OAuth IDs)
- Narrow listRoles dep return type to RoleListItem (projected fields only)
- Refactor validateDescription to early-return style per AGENTS.md
- Remove redundant double .lean() in updateRoleByName
- Document rename snapshot race window in renameRole JSDoc
- Document cache null-set behavior in deleteRoleByName
- Add routing-coupling comment on RESERVED_ROLE_NAMES
- Add test for addRoleMember post-write rollback
* fix: review cleanup — system-role guard, type safety, JSDoc accuracy, tests
- Add system-role guard to addRoleMember: block direct assignment to
non-ADMIN system roles (403), symmetric with removeRoleMember
- Fix RESERVED_ROLE_NAMES comment: explain semantic URL ambiguity, not
a routing conflict (Express resolves single vs multi-segment correctly)
- Replace _id: unknown with Types.ObjectId | string per AGENTS.md
- Narrow listRoles data-layer return type to Pick<IRole, 'name' | 'description'>
to match the actual .select() projection
- Move updateRoleHandler param check inside try/catch for consistency
- Include user IDs in all CRITICAL rollback failure logs for operator recovery
- Clarify deleteRoleByName JSDoc: replace "self-healing" with "idempotent",
document that recovery requires caller retry
- Add tests: system-role guard, promote non-admin to ADMIN,
findUserIdsByRole throw prevents migration
* fix: include _id in listRoles return type to match RoleListItem
Pick<IRole, 'name' | 'description'> omits _id, making it incompatible
with the handler dep's RoleListItem which requires _id.
* fix: case-insensitive system role guard, reject null permissions, check updateUser result
- System role name checks now use case-insensitive comparison via
toUpperCase() — prevents creating 'admin' or 'user' which would
collide with the legacy roles route that uppercases params
- Reject permissions: null in createRole (typeof null === 'object'
was bypassing the validation)
- Check updateUser return in addRoleMember — return 404 if the user
was deleted between the findUser and updateUser calls
* fix: check updateUser return in removeRoleMember for concurrent delete safety
---------
Co-authored-by: Danny Avila <danny@librechat.ai>
2026-03-27 12:44:47 -07:00
|
|
|
createRoleByName = methods.createRoleByName;
|
|
|
|
|
deleteRoleByName = methods.deleteRoleByName;
|
|
|
|
|
updateRoleByName = methods.updateRoleByName;
|
|
|
|
|
updateUsersByRole = methods.updateUsersByRole;
|
|
|
|
|
listUsersByRole = methods.listUsersByRole;
|
|
|
|
|
countUsersByRole = methods.countUsersByRole;
|
|
|
|
|
listRoles = methods.listRoles;
|
|
|
|
|
countRoles = methods.countRoles;
|
2024-08-26 15:34:46 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
afterAll(async () => {
|
|
|
|
|
await mongoose.disconnect();
|
|
|
|
|
await mongoServer.stop();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
beforeEach(async () => {
|
|
|
|
|
await Role.deleteMany({});
|
🪪 feat: Admin Roles API Endpoints (#12400)
* feat: add createRole and deleteRole methods to role
* feat: add admin roles handler factory and Express routes
* fix: address convention violations in admin roles handlers
* fix: rename createRole/deleteRole to avoid AccessRole name collision
The existing accessRole.ts already exports createRole/deleteRole for the
AccessRole model. In createMethods index.ts, these are spread after
roleMethods, overwriting them. Renamed our Role methods to
createRoleByName/deleteRoleByName to match the existing pattern
(getRoleByName, updateRoleByName) and avoid the collision.
* feat: add description field to Role model
- Add description to IRole, CreateRoleRequest, UpdateRoleRequest types
- Add description field to Mongoose roleSchema (default: '')
- Wire description through createRoleHandler and updateRoleHandler
- Include description in listRoles select clause so it appears in list
* fix: address Copilot review findings in admin roles handlers
* test: add unit tests for admin roles and groups handlers
* test: add data-layer tests for createRoleByName, deleteRoleByName, listUsersByRole
* fix: allow system role updates when name is unchanged
The updateRoleHandler guard rejected any request where body.name matched
a system role, even when the name was not being changed. This blocked
editing a system role's description. Compare against the URL param to
only reject actual renames to reserved names.
* fix: address external review findings for admin roles
- Block renaming system roles (ADMIN/USER) and add user migration on rename
- Add input validation: name max-length, trim on update, duplicate name check
- Replace fragile String.includes error matching with prefix-based classification
- Catch MongoDB 11000 duplicate key in createRoleByName
- Add pagination (limit/offset/total) to getRoleMembersHandler
- Reverse delete order in deleteRoleByName — reassign users before deletion
- Add role existence check in removeRoleMember; drop unused createdAt select
- Add Array.isArray guard for permissions input; use consistent ?? coalescing
- Fix import ordering per AGENTS.md conventions
- Type-cast mongoose.models.User as Model<IUser> for proper TS inference
- Add comprehensive tests: rename guards, pagination, validation, 500 paths
* fix: address re-review findings for admin roles
- Gate deleteRoleByName on existence check — skip user reassignment and
cache invalidation when role doesn't exist (fixes test mismatch)
- Reverse rename order: migrate users before renaming role so a migration
failure leaves the system in a consistent state
- Add .sort({ _id: 1 }) to listUsersByRole for deterministic pagination
- Import shared AdminMember type from data-schemas instead of local copy;
make joinedAt optional since neither groups nor roles populate it
- Change IRole.description from optional to required to match schema default
- Add data-layer tests for updateUsersByRole and countUsersByRole
- Add handler test verifying users-first rename ordering and migration
failure safety
* fix: add rollback on rename failure and update PR description
- Roll back user migration if updateRoleByName returns null during a
rename (race: role deleted between existence check and update)
- Add test verifying rollback calls updateUsersByRole in reverse
- Update PR #12400 description to reflect current test counts (56
handler tests, 40 data-layer tests) and safety features
* fix: rollback on rename throw, description validation, delete/DRY cleanup
- Hoist isRename/trimmedName above try block so catch can roll back user
migration when updateRoleByName throws (not just returns null)
- Add description type + max-length (2000) validation in create and update,
consistent with groups handler
- Remove redundant getRoleByName existence check in deleteRoleHandler —
use deleteRoleByName return value directly
- Skip no-op name write when body.name equals current name (use isRename)
- Extract getUserModel() accessor to DRY repeated Model<IUser> casts
- Use name.trim() consistently in createRoleByName error messages
- Add tests: rename-throw rollback, description validation (create+update),
update delete test mocks to match simplified handler
* fix: guard spurious rollback, harden createRole error path, validate before DB calls
- Add migrationRan flag to prevent rollback of user migration that never ran
- Return generic message on 500 in createRoleHandler, specific only for 409
- Move description validation before DB queries in updateRoleHandler
- Return existing role early when update body has no changes
- Wrap cache.set in createRoleByName with try/catch to prevent masking DB success
- Add JSDoc on 11000 catch explaining compound unique index
- Add tests: spurious rollback guard, empty update body, description validation
ordering, listUsersByRole pagination
* fix: validate permissions in create, RoleConflictError, rollback safety, cache consistency
- Add permissions type/array validation in createRoleHandler
- Introduce RoleConflictError class replacing fragile string-prefix matching
- Wrap rollback in !role null path with try/catch for correct 404 response
- Wrap deleteRoleByName cache.set in try/catch matching createRoleByName
- Narrow updateRoleHandler body type to { name?, description? }
- Add tests: non-string description in create, rollback failure logging,
permissions array rejection, description max-length assertion fix
* feat: prevent removing the last admin user
Add guard in removeRoleMember that checks countUsersByRole before
demoting an ADMIN user, returning 400 if they are the last one.
* fix: move interleaved export below imports, add await to countUsersByRole
* fix: paginate listRoles, null-guard permissions handler, fix export ordering
- Add limit/offset/total pagination to listRoles matching the groups pattern
- Add countRoles data-layer method
- Omit permissions from listRoles select (getRole returns full document)
- Null-guard re-fetched role in updateRolePermissionsHandler
- Move interleaved export below all imports in methods/index.ts
* fix: address review findings — race safety, validation DRY, type accuracy, test coverage
- Add post-write admin count verification in removeRoleMember to prevent
zero-admin race condition (TOCTOU → rollback if count hits 0)
- Make IRole.description optional; backfill in initializeRoles for
pre-existing roles that lack the field (.lean() bypasses defaults)
- Extract parsePagination, validateNameParam, validateRoleName, and
validateDescription helpers to eliminate duplicated validation
- Add validateNameParam guard to all 7 handlers reading req.params.name
- Catch 11000 in updateRoleByName and surface as 409 via RoleConflictError
- Add idempotent skip in addRoleMember when user already has target role
- Verify updateRolePermissions test asserts response body
- Add data-layer tests: listRoles sort/pagination/projection, countRoles,
and createRoleByName 11000 duplicate key race
* fix: defensive rollback in removeRoleMember, type/style cleanup, test coverage
- Wrap removeRoleMember post-write admin rollback in try/catch so a
transient DB failure cannot leave the system with zero administrators
- Replace double `as unknown[] as IRole[]` cast with `.lean<IRole[]>()`
- Type parsePagination param explicitly; extract DEFAULT/MAX page constants
- Preserve original error cause in updateRoleByName re-throw
- Add test for rollback failure path in removeRoleMember (returns 400)
- Add test for pre-existing roles missing description field (.lean())
* chore: bump @librechat/data-schemas to 0.0.47
* fix: stale cache on rename, extract renameRole helper, shared pagination, cleanup
- Fix updateRoleByName cache bug: invalidate old key and populate new key
when updates.name differs from roleName (prevents stale cache after rename)
- Extract renameRole helper to eliminate mutable outer-scope state flags
(isRename, trimmedName, migrationRan) in updateRoleHandler
- Unify system-role protection to 403 for both rename-from and rename-to
- Extract parsePagination to shared admin/pagination.ts; use in both
roles.ts and groups.ts
- Extract name.trim() to local const in createRoleByName (was called 5×)
- Remove redundant findOne pre-check in deleteRoleByName
- Replace getUserModel closure with local const declarations
- Remove redundant description ?? '' in createRoleHandler (schema default)
- Add doc comment on updateRolePermissionsHandler noting cache dependency
- Add data-layer tests for cache rename behavior (old key null, new key set)
* fix: harden role guards, add User.role index, validate names, improve tests
- Add index on User.role field for efficient member queries at scale
- Replace fragile SystemRoles key lookup with value-based Set check (6 sites)
- Elevate rename rollback failure logging to CRITICAL (matches removeRoleMember)
- Guard removeRoleMember against non-ADMIN system roles (403 for USER)
- Fix parsePagination limit=0 gotcha: use parseInt + NaN check instead of ||
- Add control character and reserved path segment validation to role names
- Simplify validateRoleName: remove redundant casts and dead conditions
- Add JSDoc to deleteRoleByName documenting non-atomic window
- Split mixed value+type import in methods/index.ts per AGENTS.md
- Add 9 new tests: permissions assertion, combined rename+desc, createRole
with permissions, pagination edge cases, control char/reserved name
rejection, system role removeRoleMember guard
* fix: exact-case reserved name check, consistent validation, cleaner createRole
- Remove .toLowerCase() from reserved name check so only exact matches
(members, permissions) are rejected, not legitimate names like "Members"
- Extract trimmed const in validateRoleName for consistent validation
- Add control char check to validateNameParam for parity with body validation
- Build createRole roleData conditionally to avoid passing description: undefined
- Expand deleteRoleByName JSDoc documenting self-healing design and no-op trade-off
* fix: scope rename rollback to only migrated users, prevent cross-role corruption
Capture user IDs before forward migration so the rollback path only
reverts users this request actually moved. Previously the rollback called
updateUsersByRole(newName, currentName) which would sweep all users with
the new role — including any independently assigned by a concurrent admin
request — causing silent cross-role data corruption.
Adds findUserIdsByRole and updateUsersRoleByIds to the data layer.
Extracts rollbackMigratedUsers helper to deduplicate rollback sites.
* fix: guard last admin in addRoleMember to prevent zero-admin lockout
Since each user has exactly one role, addRoleMember implicitly removes
the user from their current role. Without a guard, reassigning the sole
admin to a non-admin role leaves zero admins and locks out admin
management. Adds the same countUsersByRole check used in removeRoleMember.
* fix: wire findUserIdsByRole and updateUsersRoleByIds into roles route
The scoped rollback deps added in c89b5db were missing from the route
DI wiring, causing renameRole to call undefined and return a 500.
* fix: post-write admin guard in addRoleMember, compound role index, review cleanup
- Add post-write admin count check + rollback to addRoleMember to match
removeRoleMember's two-phase TOCTOU protection (prevents zero-admin via
concurrent requests)
- Replace single-field User.role index with compound { role: 1, tenantId: 1 }
to align with existing multi-tenant index pattern (email, OAuth IDs)
- Narrow listRoles dep return type to RoleListItem (projected fields only)
- Refactor validateDescription to early-return style per AGENTS.md
- Remove redundant double .lean() in updateRoleByName
- Document rename snapshot race window in renameRole JSDoc
- Document cache null-set behavior in deleteRoleByName
- Add routing-coupling comment on RESERVED_ROLE_NAMES
- Add test for addRoleMember post-write rollback
* fix: review cleanup — system-role guard, type safety, JSDoc accuracy, tests
- Add system-role guard to addRoleMember: block direct assignment to
non-ADMIN system roles (403), symmetric with removeRoleMember
- Fix RESERVED_ROLE_NAMES comment: explain semantic URL ambiguity, not
a routing conflict (Express resolves single vs multi-segment correctly)
- Replace _id: unknown with Types.ObjectId | string per AGENTS.md
- Narrow listRoles data-layer return type to Pick<IRole, 'name' | 'description'>
to match the actual .select() projection
- Move updateRoleHandler param check inside try/catch for consistency
- Include user IDs in all CRITICAL rollback failure logs for operator recovery
- Clarify deleteRoleByName JSDoc: replace "self-healing" with "idempotent",
document that recovery requires caller retry
- Add tests: system-role guard, promote non-admin to ADMIN,
findUserIdsByRole throw prevents migration
* fix: include _id in listRoles return type to match RoleListItem
Pick<IRole, 'name' | 'description'> omits _id, making it incompatible
with the handler dep's RoleListItem which requires _id.
* fix: case-insensitive system role guard, reject null permissions, check updateUser result
- System role name checks now use case-insensitive comparison via
toUpperCase() — prevents creating 'admin' or 'user' which would
collide with the legacy roles route that uppercases params
- Reject permissions: null in createRole (typeof null === 'object'
was bypassing the validation)
- Check updateUser return in addRoleMember — return 404 if the user
was deleted between the findUser and updateUser calls
* fix: check updateUser return in removeRoleMember for concurrent delete safety
---------
Co-authored-by: Danny Avila <danny@librechat.ai>
2026-03-27 12:44:47 -07:00
|
|
|
await User.deleteMany({});
|
📦 refactor: Consolidate DB models, encapsulating Mongoose usage in `data-schemas` (#11830)
* chore: move database model methods to /packages/data-schemas
* chore: add TypeScript ESLint rule to warn on unused variables
* refactor: model imports to streamline access
- Consolidated model imports across various files to improve code organization and reduce redundancy.
- Updated imports for models such as Assistant, Message, Conversation, and others to a unified import path.
- Adjusted middleware and service files to reflect the new import structure, ensuring functionality remains intact.
- Enhanced test files to align with the new import paths, maintaining test coverage and integrity.
* chore: migrate database models to packages/data-schemas and refactor all direct Mongoose Model usage outside of data-schemas
* test: update agent model mocks in unit tests
- Added `getAgent` mock to `client.test.js` to enhance test coverage for agent-related functionality.
- Removed redundant `getAgent` and `getAgents` mocks from `openai.spec.js` and `responses.unit.spec.js` to streamline test setup and reduce duplication.
- Ensured consistency in agent mock implementations across test files.
* fix: update types in data-schemas
* refactor: enhance type definitions in transaction and spending methods
- Updated type definitions in `checkBalance.ts` to use specific request and response types.
- Refined `spendTokens.ts` to utilize a new `SpendTxData` interface for better clarity and type safety.
- Improved transaction handling in `transaction.ts` by introducing `TransactionResult` and `TxData` interfaces, ensuring consistent data structures across methods.
- Adjusted unit tests in `transaction.spec.ts` to accommodate new type definitions and enhance robustness.
* refactor: streamline model imports and enhance code organization
- Consolidated model imports across various controllers and services to a unified import path, improving code clarity and reducing redundancy.
- Updated multiple files to reflect the new import structure, ensuring all functionalities remain intact.
- Enhanced overall code organization by removing duplicate import statements and optimizing the usage of model methods.
* feat: implement loadAddedAgent and refactor agent loading logic
- Introduced `loadAddedAgent` function to handle loading agents from added conversations, supporting multi-convo parallel execution.
- Created a new `load.ts` file to encapsulate agent loading functionalities, including `loadEphemeralAgent` and `loadAgent`.
- Updated the `index.ts` file to export the new `load` module instead of the deprecated `loadAgent`.
- Enhanced type definitions and improved error handling in the agent loading process.
- Adjusted unit tests to reflect changes in the agent loading structure and ensure comprehensive coverage.
* refactor: enhance balance handling with new update interface
- Introduced `IBalanceUpdate` interface to streamline balance update operations across the codebase.
- Updated `upsertBalanceFields` method signatures in `balance.ts`, `transaction.ts`, and related tests to utilize the new interface for improved type safety.
- Adjusted type imports in `balance.spec.ts` to include `IBalanceUpdate`, ensuring consistency in balance management functionalities.
- Enhanced overall code clarity and maintainability by refining type definitions related to balance operations.
* feat: add unit tests for loadAgent functionality and enhance agent loading logic
- Introduced comprehensive unit tests for the `loadAgent` function, covering various scenarios including null and empty agent IDs, loading of ephemeral agents, and permission checks.
- Enhanced the `initializeClient` function by moving `getConvoFiles` to the correct position in the database method exports, ensuring proper functionality.
- Improved test coverage for agent loading, including handling of non-existent agents and user permissions.
* chore: reorder memory method exports for consistency
- Moved `deleteAllUserMemories` to the correct position in the exported memory methods, ensuring a consistent and logical order of method exports in `memory.ts`.
2026-02-17 18:23:44 -05:00
|
|
|
mockGetCache.mockClear();
|
|
|
|
|
mockCache.get.mockClear();
|
|
|
|
|
mockCache.set.mockClear();
|
|
|
|
|
mockCache.del.mockClear();
|
2024-08-26 15:34:46 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('updateAccessPermissions', () => {
|
|
|
|
|
it('should update permissions when changes are needed', async () => {
|
|
|
|
|
await new Role({
|
|
|
|
|
name: SystemRoles.USER,
|
2025-04-05 01:47:14 +02:00
|
|
|
permissions: {
|
|
|
|
|
[PermissionTypes.PROMPTS]: {
|
|
|
|
|
CREATE: true,
|
|
|
|
|
USE: true,
|
2026-01-10 14:02:56 -05:00
|
|
|
SHARE: false,
|
2025-04-05 01:47:14 +02:00
|
|
|
},
|
2024-08-26 15:34:46 -04:00
|
|
|
},
|
|
|
|
|
}).save();
|
|
|
|
|
|
|
|
|
|
await updateAccessPermissions(SystemRoles.USER, {
|
|
|
|
|
[PermissionTypes.PROMPTS]: {
|
|
|
|
|
CREATE: true,
|
|
|
|
|
USE: true,
|
2026-01-10 14:02:56 -05:00
|
|
|
SHARE: true,
|
2024-08-26 15:34:46 -04:00
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
2025-04-05 01:47:14 +02:00
|
|
|
const updatedRole = await getRoleByName(SystemRoles.USER);
|
|
|
|
|
expect(updatedRole.permissions[PermissionTypes.PROMPTS]).toEqual({
|
2024-08-26 15:34:46 -04:00
|
|
|
CREATE: true,
|
|
|
|
|
USE: true,
|
2026-01-10 14:02:56 -05:00
|
|
|
SHARE: true,
|
2024-08-26 15:34:46 -04:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should not update permissions when no changes are needed', async () => {
|
|
|
|
|
await new Role({
|
|
|
|
|
name: SystemRoles.USER,
|
2025-04-05 01:47:14 +02:00
|
|
|
permissions: {
|
|
|
|
|
[PermissionTypes.PROMPTS]: {
|
|
|
|
|
CREATE: true,
|
|
|
|
|
USE: true,
|
2026-01-10 14:02:56 -05:00
|
|
|
SHARE: false,
|
2025-04-05 01:47:14 +02:00
|
|
|
},
|
2024-08-26 15:34:46 -04:00
|
|
|
},
|
|
|
|
|
}).save();
|
|
|
|
|
|
|
|
|
|
await updateAccessPermissions(SystemRoles.USER, {
|
|
|
|
|
[PermissionTypes.PROMPTS]: {
|
|
|
|
|
CREATE: true,
|
|
|
|
|
USE: true,
|
2026-01-10 14:02:56 -05:00
|
|
|
SHARE: false,
|
2024-08-26 15:34:46 -04:00
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
2025-04-05 01:47:14 +02:00
|
|
|
const updatedRole = await getRoleByName(SystemRoles.USER);
|
|
|
|
|
expect(updatedRole.permissions[PermissionTypes.PROMPTS]).toEqual({
|
2024-08-26 15:34:46 -04:00
|
|
|
CREATE: true,
|
|
|
|
|
USE: true,
|
2026-01-10 14:02:56 -05:00
|
|
|
SHARE: false,
|
2024-08-26 15:34:46 -04:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should handle non-existent roles', async () => {
|
|
|
|
|
await updateAccessPermissions('NON_EXISTENT_ROLE', {
|
2025-04-05 01:47:14 +02:00
|
|
|
[PermissionTypes.PROMPTS]: { CREATE: true },
|
2024-08-26 15:34:46 -04:00
|
|
|
});
|
|
|
|
|
const role = await Role.findOne({ name: 'NON_EXISTENT_ROLE' });
|
|
|
|
|
expect(role).toBeNull();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should update only specified permissions', async () => {
|
|
|
|
|
await new Role({
|
|
|
|
|
name: SystemRoles.USER,
|
2025-04-05 01:47:14 +02:00
|
|
|
permissions: {
|
|
|
|
|
[PermissionTypes.PROMPTS]: {
|
|
|
|
|
CREATE: true,
|
|
|
|
|
USE: true,
|
2026-01-10 14:02:56 -05:00
|
|
|
SHARE: false,
|
2025-04-05 01:47:14 +02:00
|
|
|
},
|
2024-08-26 15:34:46 -04:00
|
|
|
},
|
|
|
|
|
}).save();
|
|
|
|
|
|
|
|
|
|
await updateAccessPermissions(SystemRoles.USER, {
|
2026-01-10 14:02:56 -05:00
|
|
|
[PermissionTypes.PROMPTS]: { SHARE: true },
|
2024-08-26 15:34:46 -04:00
|
|
|
});
|
|
|
|
|
|
2025-04-05 01:47:14 +02:00
|
|
|
const updatedRole = await getRoleByName(SystemRoles.USER);
|
|
|
|
|
expect(updatedRole.permissions[PermissionTypes.PROMPTS]).toEqual({
|
2024-08-26 15:34:46 -04:00
|
|
|
CREATE: true,
|
|
|
|
|
USE: true,
|
2026-01-10 14:02:56 -05:00
|
|
|
SHARE: true,
|
2024-08-26 15:34:46 -04:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should handle partial updates', async () => {
|
|
|
|
|
await new Role({
|
|
|
|
|
name: SystemRoles.USER,
|
2025-04-05 01:47:14 +02:00
|
|
|
permissions: {
|
|
|
|
|
[PermissionTypes.PROMPTS]: {
|
|
|
|
|
CREATE: true,
|
|
|
|
|
USE: true,
|
2026-01-10 14:02:56 -05:00
|
|
|
SHARE: false,
|
2025-04-05 01:47:14 +02:00
|
|
|
},
|
2024-08-26 15:34:46 -04:00
|
|
|
},
|
|
|
|
|
}).save();
|
|
|
|
|
|
|
|
|
|
await updateAccessPermissions(SystemRoles.USER, {
|
2025-04-05 01:47:14 +02:00
|
|
|
[PermissionTypes.PROMPTS]: { USE: false },
|
2024-08-26 15:34:46 -04:00
|
|
|
});
|
|
|
|
|
|
2025-04-05 01:47:14 +02:00
|
|
|
const updatedRole = await getRoleByName(SystemRoles.USER);
|
|
|
|
|
expect(updatedRole.permissions[PermissionTypes.PROMPTS]).toEqual({
|
2024-08-26 15:34:46 -04:00
|
|
|
CREATE: true,
|
|
|
|
|
USE: false,
|
2026-01-10 14:02:56 -05:00
|
|
|
SHARE: false,
|
2024-08-26 15:34:46 -04:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should update multiple permission types at once', async () => {
|
|
|
|
|
await new Role({
|
|
|
|
|
name: SystemRoles.USER,
|
2025-04-05 01:47:14 +02:00
|
|
|
permissions: {
|
2026-01-10 14:02:56 -05:00
|
|
|
[PermissionTypes.PROMPTS]: { CREATE: true, USE: true, SHARE: false },
|
2025-04-05 01:47:14 +02:00
|
|
|
[PermissionTypes.BOOKMARKS]: { USE: true },
|
2024-08-26 15:34:46 -04:00
|
|
|
},
|
|
|
|
|
}).save();
|
|
|
|
|
|
|
|
|
|
await updateAccessPermissions(SystemRoles.USER, {
|
2026-01-10 14:02:56 -05:00
|
|
|
[PermissionTypes.PROMPTS]: { USE: false, SHARE: true },
|
2024-08-26 15:34:46 -04:00
|
|
|
[PermissionTypes.BOOKMARKS]: { USE: false },
|
|
|
|
|
});
|
|
|
|
|
|
2025-04-05 01:47:14 +02:00
|
|
|
const updatedRole = await getRoleByName(SystemRoles.USER);
|
|
|
|
|
expect(updatedRole.permissions[PermissionTypes.PROMPTS]).toEqual({
|
2024-08-26 15:34:46 -04:00
|
|
|
CREATE: true,
|
|
|
|
|
USE: false,
|
2026-01-10 14:02:56 -05:00
|
|
|
SHARE: true,
|
2024-08-26 15:34:46 -04:00
|
|
|
});
|
2025-04-05 01:47:14 +02:00
|
|
|
expect(updatedRole.permissions[PermissionTypes.BOOKMARKS]).toEqual({ USE: false });
|
2024-08-26 15:34:46 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should handle updates for a single permission type', async () => {
|
|
|
|
|
await new Role({
|
|
|
|
|
name: SystemRoles.USER,
|
2025-04-05 01:47:14 +02:00
|
|
|
permissions: {
|
2026-01-10 14:02:56 -05:00
|
|
|
[PermissionTypes.PROMPTS]: { CREATE: true, USE: true, SHARE: false },
|
2024-08-26 15:34:46 -04:00
|
|
|
},
|
|
|
|
|
}).save();
|
|
|
|
|
|
|
|
|
|
await updateAccessPermissions(SystemRoles.USER, {
|
2026-01-10 14:02:56 -05:00
|
|
|
[PermissionTypes.PROMPTS]: { USE: false, SHARE: true },
|
2024-08-26 15:34:46 -04:00
|
|
|
});
|
|
|
|
|
|
2025-04-05 01:47:14 +02:00
|
|
|
const updatedRole = await getRoleByName(SystemRoles.USER);
|
|
|
|
|
expect(updatedRole.permissions[PermissionTypes.PROMPTS]).toEqual({
|
2024-08-26 15:34:46 -04:00
|
|
|
CREATE: true,
|
|
|
|
|
USE: false,
|
2026-01-10 14:02:56 -05:00
|
|
|
SHARE: true,
|
2024-08-26 15:34:46 -04:00
|
|
|
});
|
|
|
|
|
});
|
2024-09-09 16:29:24 -04:00
|
|
|
|
|
|
|
|
it('should update MULTI_CONVO permissions', async () => {
|
|
|
|
|
await new Role({
|
|
|
|
|
name: SystemRoles.USER,
|
2025-04-05 01:47:14 +02:00
|
|
|
permissions: {
|
|
|
|
|
[PermissionTypes.MULTI_CONVO]: { USE: false },
|
2024-09-09 16:29:24 -04:00
|
|
|
},
|
|
|
|
|
}).save();
|
|
|
|
|
|
|
|
|
|
await updateAccessPermissions(SystemRoles.USER, {
|
2025-04-05 01:47:14 +02:00
|
|
|
[PermissionTypes.MULTI_CONVO]: { USE: true },
|
2024-09-09 16:29:24 -04:00
|
|
|
});
|
|
|
|
|
|
2025-04-05 01:47:14 +02:00
|
|
|
const updatedRole = await getRoleByName(SystemRoles.USER);
|
|
|
|
|
expect(updatedRole.permissions[PermissionTypes.MULTI_CONVO]).toEqual({ USE: true });
|
2024-09-09 16:29:24 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should update MULTI_CONVO permissions along with other permission types', async () => {
|
|
|
|
|
await new Role({
|
|
|
|
|
name: SystemRoles.USER,
|
2025-04-05 01:47:14 +02:00
|
|
|
permissions: {
|
2026-01-10 14:02:56 -05:00
|
|
|
[PermissionTypes.PROMPTS]: { CREATE: true, USE: true, SHARE: false },
|
2025-04-05 01:47:14 +02:00
|
|
|
[PermissionTypes.MULTI_CONVO]: { USE: false },
|
2024-09-09 16:29:24 -04:00
|
|
|
},
|
|
|
|
|
}).save();
|
|
|
|
|
|
|
|
|
|
await updateAccessPermissions(SystemRoles.USER, {
|
2026-01-10 14:02:56 -05:00
|
|
|
[PermissionTypes.PROMPTS]: { SHARE: true },
|
2024-09-09 16:29:24 -04:00
|
|
|
[PermissionTypes.MULTI_CONVO]: { USE: true },
|
|
|
|
|
});
|
|
|
|
|
|
2025-04-05 01:47:14 +02:00
|
|
|
const updatedRole = await getRoleByName(SystemRoles.USER);
|
|
|
|
|
expect(updatedRole.permissions[PermissionTypes.PROMPTS]).toEqual({
|
2024-09-09 16:29:24 -04:00
|
|
|
CREATE: true,
|
|
|
|
|
USE: true,
|
2026-01-10 14:02:56 -05:00
|
|
|
SHARE: true,
|
2024-09-09 16:29:24 -04:00
|
|
|
});
|
2025-04-05 01:47:14 +02:00
|
|
|
expect(updatedRole.permissions[PermissionTypes.MULTI_CONVO]).toEqual({ USE: true });
|
2024-09-09 16:29:24 -04:00
|
|
|
});
|
|
|
|
|
|
2026-02-18 12:48:33 -05:00
|
|
|
it('should inherit SHARED_GLOBAL value into SHARE when SHARE is absent from both DB and update', async () => {
|
|
|
|
|
// Simulates the startup backfill path: caller sends SHARE_PUBLIC but not SHARE;
|
|
|
|
|
// migration should inherit SHARED_GLOBAL to preserve the deployment's sharing intent.
|
|
|
|
|
await Role.collection.insertOne({
|
|
|
|
|
name: SystemRoles.USER,
|
|
|
|
|
permissions: {
|
|
|
|
|
[PermissionTypes.PROMPTS]: { USE: true, CREATE: true, SHARED_GLOBAL: true },
|
|
|
|
|
[PermissionTypes.AGENTS]: { USE: true, CREATE: true, SHARED_GLOBAL: false },
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await updateAccessPermissions(SystemRoles.USER, {
|
|
|
|
|
// No explicit SHARE — migration should inherit from SHARED_GLOBAL
|
|
|
|
|
[PermissionTypes.PROMPTS]: { SHARE_PUBLIC: false },
|
|
|
|
|
[PermissionTypes.AGENTS]: { SHARE_PUBLIC: false },
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const updatedRole = await getRoleByName(SystemRoles.USER);
|
|
|
|
|
|
|
|
|
|
// SHARED_GLOBAL=true → SHARE=true (inherited)
|
|
|
|
|
expect(updatedRole.permissions[PermissionTypes.PROMPTS].SHARE).toBe(true);
|
|
|
|
|
// SHARED_GLOBAL=false → SHARE=false (inherited)
|
|
|
|
|
expect(updatedRole.permissions[PermissionTypes.AGENTS].SHARE).toBe(false);
|
|
|
|
|
// SHARED_GLOBAL cleaned up
|
|
|
|
|
expect(updatedRole.permissions[PermissionTypes.PROMPTS].SHARED_GLOBAL).toBeUndefined();
|
|
|
|
|
expect(updatedRole.permissions[PermissionTypes.AGENTS].SHARED_GLOBAL).toBeUndefined();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should respect explicit SHARE in update payload and not override it with SHARED_GLOBAL', async () => {
|
|
|
|
|
// Caller explicitly passes SHARE: false even though SHARED_GLOBAL=true in DB.
|
|
|
|
|
// The explicit intent must win; migration must not silently overwrite it.
|
|
|
|
|
await Role.collection.insertOne({
|
|
|
|
|
name: SystemRoles.USER,
|
|
|
|
|
permissions: {
|
|
|
|
|
[PermissionTypes.PROMPTS]: { USE: true, SHARED_GLOBAL: true },
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await updateAccessPermissions(SystemRoles.USER, {
|
|
|
|
|
[PermissionTypes.PROMPTS]: { SHARE: false }, // explicit false — should be preserved
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const updatedRole = await getRoleByName(SystemRoles.USER);
|
|
|
|
|
|
|
|
|
|
expect(updatedRole.permissions[PermissionTypes.PROMPTS].SHARE).toBe(false);
|
|
|
|
|
expect(updatedRole.permissions[PermissionTypes.PROMPTS].SHARED_GLOBAL).toBeUndefined();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should migrate SHARED_GLOBAL to SHARE even when the permType is not in the update payload', async () => {
|
|
|
|
|
// Bug #2 regression: cleanup block removes SHARED_GLOBAL but migration block only
|
|
|
|
|
// runs when the permType is in the update payload. Without the fix, SHARE would be
|
|
|
|
|
// lost when any other permType (e.g. MULTI_CONVO) is the only thing being updated.
|
|
|
|
|
await Role.collection.insertOne({
|
|
|
|
|
name: SystemRoles.USER,
|
|
|
|
|
permissions: {
|
|
|
|
|
[PermissionTypes.PROMPTS]: {
|
|
|
|
|
USE: true,
|
|
|
|
|
SHARED_GLOBAL: true, // legacy — NO SHARE present
|
|
|
|
|
},
|
|
|
|
|
[PermissionTypes.MULTI_CONVO]: { USE: false },
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Only update MULTI_CONVO — PROMPTS is intentionally absent from the payload
|
|
|
|
|
await updateAccessPermissions(SystemRoles.USER, {
|
|
|
|
|
[PermissionTypes.MULTI_CONVO]: { USE: true },
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const updatedRole = await getRoleByName(SystemRoles.USER);
|
|
|
|
|
|
|
|
|
|
// SHARE should have been inherited from SHARED_GLOBAL, not silently dropped
|
|
|
|
|
expect(updatedRole.permissions[PermissionTypes.PROMPTS].SHARE).toBe(true);
|
|
|
|
|
// SHARED_GLOBAL should be removed
|
|
|
|
|
expect(updatedRole.permissions[PermissionTypes.PROMPTS].SHARED_GLOBAL).toBeUndefined();
|
|
|
|
|
// Original USE should be untouched
|
|
|
|
|
expect(updatedRole.permissions[PermissionTypes.PROMPTS].USE).toBe(true);
|
|
|
|
|
// The actual update should have applied
|
|
|
|
|
expect(updatedRole.permissions[PermissionTypes.MULTI_CONVO].USE).toBe(true);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should remove orphaned SHARED_GLOBAL when SHARE already exists and permType is not in update', async () => {
|
|
|
|
|
// Safe cleanup case: SHARE already set, SHARED_GLOBAL is just orphaned noise.
|
|
|
|
|
// SHARE must not be changed; SHARED_GLOBAL must be removed.
|
|
|
|
|
await Role.collection.insertOne({
|
|
|
|
|
name: SystemRoles.USER,
|
|
|
|
|
permissions: {
|
|
|
|
|
[PermissionTypes.PROMPTS]: {
|
|
|
|
|
USE: true,
|
|
|
|
|
SHARE: true, // already migrated
|
|
|
|
|
SHARED_GLOBAL: true, // orphaned
|
|
|
|
|
},
|
|
|
|
|
[PermissionTypes.MULTI_CONVO]: { USE: false },
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await updateAccessPermissions(SystemRoles.USER, {
|
|
|
|
|
[PermissionTypes.MULTI_CONVO]: { USE: true },
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const updatedRole = await getRoleByName(SystemRoles.USER);
|
|
|
|
|
|
|
|
|
|
expect(updatedRole.permissions[PermissionTypes.PROMPTS].SHARED_GLOBAL).toBeUndefined();
|
|
|
|
|
expect(updatedRole.permissions[PermissionTypes.PROMPTS].SHARE).toBe(true);
|
|
|
|
|
expect(updatedRole.permissions[PermissionTypes.MULTI_CONVO].USE).toBe(true);
|
|
|
|
|
});
|
|
|
|
|
|
2024-09-09 16:29:24 -04:00
|
|
|
it('should not update MULTI_CONVO permissions when no changes are needed', async () => {
|
|
|
|
|
await new Role({
|
|
|
|
|
name: SystemRoles.USER,
|
2025-04-05 01:47:14 +02:00
|
|
|
permissions: {
|
|
|
|
|
[PermissionTypes.MULTI_CONVO]: { USE: true },
|
2024-09-09 16:29:24 -04:00
|
|
|
},
|
|
|
|
|
}).save();
|
|
|
|
|
|
|
|
|
|
await updateAccessPermissions(SystemRoles.USER, {
|
2025-04-05 01:47:14 +02:00
|
|
|
[PermissionTypes.MULTI_CONVO]: { USE: true },
|
2024-09-09 16:29:24 -04:00
|
|
|
});
|
|
|
|
|
|
2025-04-05 01:47:14 +02:00
|
|
|
const updatedRole = await getRoleByName(SystemRoles.USER);
|
|
|
|
|
expect(updatedRole.permissions[PermissionTypes.MULTI_CONVO]).toEqual({ USE: true });
|
2024-09-09 16:29:24 -04:00
|
|
|
});
|
2024-08-26 15:34:46 -04:00
|
|
|
});
|
🪨 feat: AWS Bedrock support (#3935)
* feat: Add BedrockIcon component to SVG library
* feat: EModelEndpoint.bedrock
* feat: first pass, bedrock chat. note: AgentClient is returning `agents` as conversation.endpoint
* fix: declare endpoint in initialization step
* chore: Update @librechat/agents dependency to version 1.4.5
* feat: backend content aggregation for agents/bedrock
* feat: abort agent requests
* feat: AWS Bedrock icons
* WIP: agent provider schema parsing
* chore: Update EditIcon props type
* refactor(useGenerationsByLatest): make agents and bedrock editable
* refactor: non-assistant message content, parts
* fix: Bedrock response `sender`
* fix: use endpointOption.model_parameters not endpointOption.modelOptions
* fix: types for step handler
* refactor: Update Agents.ToolCallDelta type
* refactor: Remove unnecessary assignment of parentMessageId in AskController
* refactor: remove unnecessary assignment of parentMessageId (agent request handler)
* fix(bedrock/agents): message regeneration
* refactor: dynamic form elements using react-hook-form Controllers
* fix: agent icons/labels for messages
* fix: agent actions
* fix: use of new dynamic tags causing application crash
* refactor: dynamic settings touch-ups
* refactor: update Slider component to allow custom track class name
* refactor: update DynamicSlider component styles
* refactor: use Constants value for GLOBAL_PROJECT_NAME (enum)
* feat: agent share global methods/controllers
* fix: agents query
* fix: `getResponseModel`
* fix: share prompt a11y issue
* refactor: update SharePrompt dialog theme styles
* refactor: explicit typing for SharePrompt
* feat: add agent roles/permissions
* chore: update @librechat/agents dependency to version 1.4.7 for tool_call_ids edge case
* fix(Anthropic): messages.X.content.Y.tool_use.input: Input should be a valid dictionary
* fix: handle text parts with tool_call_ids and empty text
* fix: role initialization
* refactor: don't make instructions required
* refactor: improve typing of Text part
* fix: setShowStopButton for agents route
* chore: remove params for now
* fix: add streamBuffer and streamRate to help prevent 'Overloaded' errors from Anthropic API
* refactor: remove console.log statement in ContentRender component
* chore: typing, rename Context to Delete Button
* chore(DeleteButton): logging
* refactor(Action): make accessible
* style(Action): improve a11y again
* refactor: remove use/mention of mongoose sessions
* feat: first pass, sharing agents
* feat: visual indicator for global agent, remove author when serving to non-author
* wip: params
* chore: fix typing issues
* fix(schemas): typing
* refactor: improve accessibility of ListCard component and fix console React warning
* wip: reset templates for non-legacy new convos
* Revert "wip: params"
This reverts commit f8067e91d4adf7be9e0d9e914aaae79ac4689b80.
* Revert "refactor: dynamic form elements using react-hook-form Controllers"
This reverts commit 2150c4815d8c74a978a4b697aa8f54dc11e035d7.
* fix(Parameters): types and parameter effect update to only update local state to parameters
* refactor: optimize useDebouncedInput hook for better performance
* feat: first pass, anthropic bedrock params
* chore: paramEndpoints check for endpointType too
* fix: maxTokens to use coerceNumber.optional(),
* feat: extra chat model params
* chore: reduce code repetition
* refactor: improve preset title handling in SaveAsPresetDialog component
* refactor: improve preset handling in HeaderOptions component
* chore: improve typing, replace legacy dialog for SaveAsPresetDialog
* feat: save as preset from parameters panel
* fix: multi-search in select dropdown when using Option type
* refactor: update default showDefault value to false in Dynamic components
* feat: Bedrock presets settings
* chore: config, fix agents schema, update config version
* refactor: update AWS region variable name in bedrock options endpoint to BEDROCK_AWS_DEFAULT_REGION
* refactor: update baseEndpointSchema in config.ts to include baseURL property
* refactor: update createRun function to include req parameter and set streamRate based on provider
* feat: availableRegions via config
* refactor: remove unused demo agent controller file
* WIP: title
* Update @librechat/agents to version 1.5.0
* chore: addTitle.js to handle empty responseText
* feat: support images and titles
* feat: context token updates
* Refactor BaseClient test to use expect.objectContaining
* refactor: add model select, remove header options params, move side panel params below prompts
* chore: update models list, catch title error
* feat: model service for bedrock models (env)
* chore: Remove verbose debug log in AgentClient class following stream
* feat(bedrock): track token spend; fix: token rates, value key mapping for AWS models
* refactor: handle streamRate in `handleLLMNewToken` callback
* chore: AWS Bedrock example config in `.env.example`
* refactor: Rename bedrockMeta to bedrockGeneral in settings.ts and use for AI21 and Amazon Bedrock providers
* refactor: Update `.env.example` with AWS Bedrock model IDs URL and additional notes
* feat: titleModel support for bedrock
* refactor: Update `.env.example` with additional notes for AWS Bedrock model IDs
2024-09-09 12:06:59 -04:00
|
|
|
|
|
|
|
|
describe('initializeRoles', () => {
|
|
|
|
|
beforeEach(async () => {
|
|
|
|
|
await Role.deleteMany({});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should create default roles if they do not exist', async () => {
|
|
|
|
|
await initializeRoles();
|
|
|
|
|
|
2025-04-05 01:47:14 +02:00
|
|
|
const adminRole = await getRoleByName(SystemRoles.ADMIN);
|
|
|
|
|
const userRole = await getRoleByName(SystemRoles.USER);
|
🪨 feat: AWS Bedrock support (#3935)
* feat: Add BedrockIcon component to SVG library
* feat: EModelEndpoint.bedrock
* feat: first pass, bedrock chat. note: AgentClient is returning `agents` as conversation.endpoint
* fix: declare endpoint in initialization step
* chore: Update @librechat/agents dependency to version 1.4.5
* feat: backend content aggregation for agents/bedrock
* feat: abort agent requests
* feat: AWS Bedrock icons
* WIP: agent provider schema parsing
* chore: Update EditIcon props type
* refactor(useGenerationsByLatest): make agents and bedrock editable
* refactor: non-assistant message content, parts
* fix: Bedrock response `sender`
* fix: use endpointOption.model_parameters not endpointOption.modelOptions
* fix: types for step handler
* refactor: Update Agents.ToolCallDelta type
* refactor: Remove unnecessary assignment of parentMessageId in AskController
* refactor: remove unnecessary assignment of parentMessageId (agent request handler)
* fix(bedrock/agents): message regeneration
* refactor: dynamic form elements using react-hook-form Controllers
* fix: agent icons/labels for messages
* fix: agent actions
* fix: use of new dynamic tags causing application crash
* refactor: dynamic settings touch-ups
* refactor: update Slider component to allow custom track class name
* refactor: update DynamicSlider component styles
* refactor: use Constants value for GLOBAL_PROJECT_NAME (enum)
* feat: agent share global methods/controllers
* fix: agents query
* fix: `getResponseModel`
* fix: share prompt a11y issue
* refactor: update SharePrompt dialog theme styles
* refactor: explicit typing for SharePrompt
* feat: add agent roles/permissions
* chore: update @librechat/agents dependency to version 1.4.7 for tool_call_ids edge case
* fix(Anthropic): messages.X.content.Y.tool_use.input: Input should be a valid dictionary
* fix: handle text parts with tool_call_ids and empty text
* fix: role initialization
* refactor: don't make instructions required
* refactor: improve typing of Text part
* fix: setShowStopButton for agents route
* chore: remove params for now
* fix: add streamBuffer and streamRate to help prevent 'Overloaded' errors from Anthropic API
* refactor: remove console.log statement in ContentRender component
* chore: typing, rename Context to Delete Button
* chore(DeleteButton): logging
* refactor(Action): make accessible
* style(Action): improve a11y again
* refactor: remove use/mention of mongoose sessions
* feat: first pass, sharing agents
* feat: visual indicator for global agent, remove author when serving to non-author
* wip: params
* chore: fix typing issues
* fix(schemas): typing
* refactor: improve accessibility of ListCard component and fix console React warning
* wip: reset templates for non-legacy new convos
* Revert "wip: params"
This reverts commit f8067e91d4adf7be9e0d9e914aaae79ac4689b80.
* Revert "refactor: dynamic form elements using react-hook-form Controllers"
This reverts commit 2150c4815d8c74a978a4b697aa8f54dc11e035d7.
* fix(Parameters): types and parameter effect update to only update local state to parameters
* refactor: optimize useDebouncedInput hook for better performance
* feat: first pass, anthropic bedrock params
* chore: paramEndpoints check for endpointType too
* fix: maxTokens to use coerceNumber.optional(),
* feat: extra chat model params
* chore: reduce code repetition
* refactor: improve preset title handling in SaveAsPresetDialog component
* refactor: improve preset handling in HeaderOptions component
* chore: improve typing, replace legacy dialog for SaveAsPresetDialog
* feat: save as preset from parameters panel
* fix: multi-search in select dropdown when using Option type
* refactor: update default showDefault value to false in Dynamic components
* feat: Bedrock presets settings
* chore: config, fix agents schema, update config version
* refactor: update AWS region variable name in bedrock options endpoint to BEDROCK_AWS_DEFAULT_REGION
* refactor: update baseEndpointSchema in config.ts to include baseURL property
* refactor: update createRun function to include req parameter and set streamRate based on provider
* feat: availableRegions via config
* refactor: remove unused demo agent controller file
* WIP: title
* Update @librechat/agents to version 1.5.0
* chore: addTitle.js to handle empty responseText
* feat: support images and titles
* feat: context token updates
* Refactor BaseClient test to use expect.objectContaining
* refactor: add model select, remove header options params, move side panel params below prompts
* chore: update models list, catch title error
* feat: model service for bedrock models (env)
* chore: Remove verbose debug log in AgentClient class following stream
* feat(bedrock): track token spend; fix: token rates, value key mapping for AWS models
* refactor: handle streamRate in `handleLLMNewToken` callback
* chore: AWS Bedrock example config in `.env.example`
* refactor: Rename bedrockMeta to bedrockGeneral in settings.ts and use for AI21 and Amazon Bedrock providers
* refactor: Update `.env.example` with AWS Bedrock model IDs URL and additional notes
* feat: titleModel support for bedrock
* refactor: Update `.env.example` with additional notes for AWS Bedrock model IDs
2024-09-09 12:06:59 -04:00
|
|
|
|
|
|
|
|
expect(adminRole).toBeTruthy();
|
|
|
|
|
expect(userRole).toBeTruthy();
|
|
|
|
|
|
2025-04-05 01:47:14 +02:00
|
|
|
// Check if all permission types exist in the permissions field
|
🪨 feat: AWS Bedrock support (#3935)
* feat: Add BedrockIcon component to SVG library
* feat: EModelEndpoint.bedrock
* feat: first pass, bedrock chat. note: AgentClient is returning `agents` as conversation.endpoint
* fix: declare endpoint in initialization step
* chore: Update @librechat/agents dependency to version 1.4.5
* feat: backend content aggregation for agents/bedrock
* feat: abort agent requests
* feat: AWS Bedrock icons
* WIP: agent provider schema parsing
* chore: Update EditIcon props type
* refactor(useGenerationsByLatest): make agents and bedrock editable
* refactor: non-assistant message content, parts
* fix: Bedrock response `sender`
* fix: use endpointOption.model_parameters not endpointOption.modelOptions
* fix: types for step handler
* refactor: Update Agents.ToolCallDelta type
* refactor: Remove unnecessary assignment of parentMessageId in AskController
* refactor: remove unnecessary assignment of parentMessageId (agent request handler)
* fix(bedrock/agents): message regeneration
* refactor: dynamic form elements using react-hook-form Controllers
* fix: agent icons/labels for messages
* fix: agent actions
* fix: use of new dynamic tags causing application crash
* refactor: dynamic settings touch-ups
* refactor: update Slider component to allow custom track class name
* refactor: update DynamicSlider component styles
* refactor: use Constants value for GLOBAL_PROJECT_NAME (enum)
* feat: agent share global methods/controllers
* fix: agents query
* fix: `getResponseModel`
* fix: share prompt a11y issue
* refactor: update SharePrompt dialog theme styles
* refactor: explicit typing for SharePrompt
* feat: add agent roles/permissions
* chore: update @librechat/agents dependency to version 1.4.7 for tool_call_ids edge case
* fix(Anthropic): messages.X.content.Y.tool_use.input: Input should be a valid dictionary
* fix: handle text parts with tool_call_ids and empty text
* fix: role initialization
* refactor: don't make instructions required
* refactor: improve typing of Text part
* fix: setShowStopButton for agents route
* chore: remove params for now
* fix: add streamBuffer and streamRate to help prevent 'Overloaded' errors from Anthropic API
* refactor: remove console.log statement in ContentRender component
* chore: typing, rename Context to Delete Button
* chore(DeleteButton): logging
* refactor(Action): make accessible
* style(Action): improve a11y again
* refactor: remove use/mention of mongoose sessions
* feat: first pass, sharing agents
* feat: visual indicator for global agent, remove author when serving to non-author
* wip: params
* chore: fix typing issues
* fix(schemas): typing
* refactor: improve accessibility of ListCard component and fix console React warning
* wip: reset templates for non-legacy new convos
* Revert "wip: params"
This reverts commit f8067e91d4adf7be9e0d9e914aaae79ac4689b80.
* Revert "refactor: dynamic form elements using react-hook-form Controllers"
This reverts commit 2150c4815d8c74a978a4b697aa8f54dc11e035d7.
* fix(Parameters): types and parameter effect update to only update local state to parameters
* refactor: optimize useDebouncedInput hook for better performance
* feat: first pass, anthropic bedrock params
* chore: paramEndpoints check for endpointType too
* fix: maxTokens to use coerceNumber.optional(),
* feat: extra chat model params
* chore: reduce code repetition
* refactor: improve preset title handling in SaveAsPresetDialog component
* refactor: improve preset handling in HeaderOptions component
* chore: improve typing, replace legacy dialog for SaveAsPresetDialog
* feat: save as preset from parameters panel
* fix: multi-search in select dropdown when using Option type
* refactor: update default showDefault value to false in Dynamic components
* feat: Bedrock presets settings
* chore: config, fix agents schema, update config version
* refactor: update AWS region variable name in bedrock options endpoint to BEDROCK_AWS_DEFAULT_REGION
* refactor: update baseEndpointSchema in config.ts to include baseURL property
* refactor: update createRun function to include req parameter and set streamRate based on provider
* feat: availableRegions via config
* refactor: remove unused demo agent controller file
* WIP: title
* Update @librechat/agents to version 1.5.0
* chore: addTitle.js to handle empty responseText
* feat: support images and titles
* feat: context token updates
* Refactor BaseClient test to use expect.objectContaining
* refactor: add model select, remove header options params, move side panel params below prompts
* chore: update models list, catch title error
* feat: model service for bedrock models (env)
* chore: Remove verbose debug log in AgentClient class following stream
* feat(bedrock): track token spend; fix: token rates, value key mapping for AWS models
* refactor: handle streamRate in `handleLLMNewToken` callback
* chore: AWS Bedrock example config in `.env.example`
* refactor: Rename bedrockMeta to bedrockGeneral in settings.ts and use for AI21 and Amazon Bedrock providers
* refactor: Update `.env.example` with AWS Bedrock model IDs URL and additional notes
* feat: titleModel support for bedrock
* refactor: Update `.env.example` with additional notes for AWS Bedrock model IDs
2024-09-09 12:06:59 -04:00
|
|
|
Object.values(PermissionTypes).forEach((permType) => {
|
2025-04-05 01:47:14 +02:00
|
|
|
expect(adminRole.permissions[permType]).toBeDefined();
|
|
|
|
|
expect(userRole.permissions[permType]).toBeDefined();
|
🪨 feat: AWS Bedrock support (#3935)
* feat: Add BedrockIcon component to SVG library
* feat: EModelEndpoint.bedrock
* feat: first pass, bedrock chat. note: AgentClient is returning `agents` as conversation.endpoint
* fix: declare endpoint in initialization step
* chore: Update @librechat/agents dependency to version 1.4.5
* feat: backend content aggregation for agents/bedrock
* feat: abort agent requests
* feat: AWS Bedrock icons
* WIP: agent provider schema parsing
* chore: Update EditIcon props type
* refactor(useGenerationsByLatest): make agents and bedrock editable
* refactor: non-assistant message content, parts
* fix: Bedrock response `sender`
* fix: use endpointOption.model_parameters not endpointOption.modelOptions
* fix: types for step handler
* refactor: Update Agents.ToolCallDelta type
* refactor: Remove unnecessary assignment of parentMessageId in AskController
* refactor: remove unnecessary assignment of parentMessageId (agent request handler)
* fix(bedrock/agents): message regeneration
* refactor: dynamic form elements using react-hook-form Controllers
* fix: agent icons/labels for messages
* fix: agent actions
* fix: use of new dynamic tags causing application crash
* refactor: dynamic settings touch-ups
* refactor: update Slider component to allow custom track class name
* refactor: update DynamicSlider component styles
* refactor: use Constants value for GLOBAL_PROJECT_NAME (enum)
* feat: agent share global methods/controllers
* fix: agents query
* fix: `getResponseModel`
* fix: share prompt a11y issue
* refactor: update SharePrompt dialog theme styles
* refactor: explicit typing for SharePrompt
* feat: add agent roles/permissions
* chore: update @librechat/agents dependency to version 1.4.7 for tool_call_ids edge case
* fix(Anthropic): messages.X.content.Y.tool_use.input: Input should be a valid dictionary
* fix: handle text parts with tool_call_ids and empty text
* fix: role initialization
* refactor: don't make instructions required
* refactor: improve typing of Text part
* fix: setShowStopButton for agents route
* chore: remove params for now
* fix: add streamBuffer and streamRate to help prevent 'Overloaded' errors from Anthropic API
* refactor: remove console.log statement in ContentRender component
* chore: typing, rename Context to Delete Button
* chore(DeleteButton): logging
* refactor(Action): make accessible
* style(Action): improve a11y again
* refactor: remove use/mention of mongoose sessions
* feat: first pass, sharing agents
* feat: visual indicator for global agent, remove author when serving to non-author
* wip: params
* chore: fix typing issues
* fix(schemas): typing
* refactor: improve accessibility of ListCard component and fix console React warning
* wip: reset templates for non-legacy new convos
* Revert "wip: params"
This reverts commit f8067e91d4adf7be9e0d9e914aaae79ac4689b80.
* Revert "refactor: dynamic form elements using react-hook-form Controllers"
This reverts commit 2150c4815d8c74a978a4b697aa8f54dc11e035d7.
* fix(Parameters): types and parameter effect update to only update local state to parameters
* refactor: optimize useDebouncedInput hook for better performance
* feat: first pass, anthropic bedrock params
* chore: paramEndpoints check for endpointType too
* fix: maxTokens to use coerceNumber.optional(),
* feat: extra chat model params
* chore: reduce code repetition
* refactor: improve preset title handling in SaveAsPresetDialog component
* refactor: improve preset handling in HeaderOptions component
* chore: improve typing, replace legacy dialog for SaveAsPresetDialog
* feat: save as preset from parameters panel
* fix: multi-search in select dropdown when using Option type
* refactor: update default showDefault value to false in Dynamic components
* feat: Bedrock presets settings
* chore: config, fix agents schema, update config version
* refactor: update AWS region variable name in bedrock options endpoint to BEDROCK_AWS_DEFAULT_REGION
* refactor: update baseEndpointSchema in config.ts to include baseURL property
* refactor: update createRun function to include req parameter and set streamRate based on provider
* feat: availableRegions via config
* refactor: remove unused demo agent controller file
* WIP: title
* Update @librechat/agents to version 1.5.0
* chore: addTitle.js to handle empty responseText
* feat: support images and titles
* feat: context token updates
* Refactor BaseClient test to use expect.objectContaining
* refactor: add model select, remove header options params, move side panel params below prompts
* chore: update models list, catch title error
* feat: model service for bedrock models (env)
* chore: Remove verbose debug log in AgentClient class following stream
* feat(bedrock): track token spend; fix: token rates, value key mapping for AWS models
* refactor: handle streamRate in `handleLLMNewToken` callback
* chore: AWS Bedrock example config in `.env.example`
* refactor: Rename bedrockMeta to bedrockGeneral in settings.ts and use for AI21 and Amazon Bedrock providers
* refactor: Update `.env.example` with AWS Bedrock model IDs URL and additional notes
* feat: titleModel support for bedrock
* refactor: Update `.env.example` with additional notes for AWS Bedrock model IDs
2024-09-09 12:06:59 -04:00
|
|
|
});
|
|
|
|
|
|
2025-04-05 01:47:14 +02:00
|
|
|
// Example: Check default values for ADMIN role
|
📦 refactor: Consolidate DB models, encapsulating Mongoose usage in `data-schemas` (#11830)
* chore: move database model methods to /packages/data-schemas
* chore: add TypeScript ESLint rule to warn on unused variables
* refactor: model imports to streamline access
- Consolidated model imports across various files to improve code organization and reduce redundancy.
- Updated imports for models such as Assistant, Message, Conversation, and others to a unified import path.
- Adjusted middleware and service files to reflect the new import structure, ensuring functionality remains intact.
- Enhanced test files to align with the new import paths, maintaining test coverage and integrity.
* chore: migrate database models to packages/data-schemas and refactor all direct Mongoose Model usage outside of data-schemas
* test: update agent model mocks in unit tests
- Added `getAgent` mock to `client.test.js` to enhance test coverage for agent-related functionality.
- Removed redundant `getAgent` and `getAgents` mocks from `openai.spec.js` and `responses.unit.spec.js` to streamline test setup and reduce duplication.
- Ensured consistency in agent mock implementations across test files.
* fix: update types in data-schemas
* refactor: enhance type definitions in transaction and spending methods
- Updated type definitions in `checkBalance.ts` to use specific request and response types.
- Refined `spendTokens.ts` to utilize a new `SpendTxData` interface for better clarity and type safety.
- Improved transaction handling in `transaction.ts` by introducing `TransactionResult` and `TxData` interfaces, ensuring consistent data structures across methods.
- Adjusted unit tests in `transaction.spec.ts` to accommodate new type definitions and enhance robustness.
* refactor: streamline model imports and enhance code organization
- Consolidated model imports across various controllers and services to a unified import path, improving code clarity and reducing redundancy.
- Updated multiple files to reflect the new import structure, ensuring all functionalities remain intact.
- Enhanced overall code organization by removing duplicate import statements and optimizing the usage of model methods.
* feat: implement loadAddedAgent and refactor agent loading logic
- Introduced `loadAddedAgent` function to handle loading agents from added conversations, supporting multi-convo parallel execution.
- Created a new `load.ts` file to encapsulate agent loading functionalities, including `loadEphemeralAgent` and `loadAgent`.
- Updated the `index.ts` file to export the new `load` module instead of the deprecated `loadAgent`.
- Enhanced type definitions and improved error handling in the agent loading process.
- Adjusted unit tests to reflect changes in the agent loading structure and ensure comprehensive coverage.
* refactor: enhance balance handling with new update interface
- Introduced `IBalanceUpdate` interface to streamline balance update operations across the codebase.
- Updated `upsertBalanceFields` method signatures in `balance.ts`, `transaction.ts`, and related tests to utilize the new interface for improved type safety.
- Adjusted type imports in `balance.spec.ts` to include `IBalanceUpdate`, ensuring consistency in balance management functionalities.
- Enhanced overall code clarity and maintainability by refining type definitions related to balance operations.
* feat: add unit tests for loadAgent functionality and enhance agent loading logic
- Introduced comprehensive unit tests for the `loadAgent` function, covering various scenarios including null and empty agent IDs, loading of ephemeral agents, and permission checks.
- Enhanced the `initializeClient` function by moving `getConvoFiles` to the correct position in the database method exports, ensuring proper functionality.
- Improved test coverage for agent loading, including handling of non-existent agents and user permissions.
* chore: reorder memory method exports for consistency
- Moved `deleteAllUserMemories` to the correct position in the exported memory methods, ensuring a consistent and logical order of method exports in `memory.ts`.
2026-02-17 18:23:44 -05:00
|
|
|
expect(adminRole.permissions[PermissionTypes.PROMPTS]?.SHARE).toBe(true);
|
|
|
|
|
expect(adminRole.permissions[PermissionTypes.BOOKMARKS]?.USE).toBe(true);
|
|
|
|
|
expect(adminRole.permissions[PermissionTypes.AGENTS]?.CREATE).toBe(true);
|
🪨 feat: AWS Bedrock support (#3935)
* feat: Add BedrockIcon component to SVG library
* feat: EModelEndpoint.bedrock
* feat: first pass, bedrock chat. note: AgentClient is returning `agents` as conversation.endpoint
* fix: declare endpoint in initialization step
* chore: Update @librechat/agents dependency to version 1.4.5
* feat: backend content aggregation for agents/bedrock
* feat: abort agent requests
* feat: AWS Bedrock icons
* WIP: agent provider schema parsing
* chore: Update EditIcon props type
* refactor(useGenerationsByLatest): make agents and bedrock editable
* refactor: non-assistant message content, parts
* fix: Bedrock response `sender`
* fix: use endpointOption.model_parameters not endpointOption.modelOptions
* fix: types for step handler
* refactor: Update Agents.ToolCallDelta type
* refactor: Remove unnecessary assignment of parentMessageId in AskController
* refactor: remove unnecessary assignment of parentMessageId (agent request handler)
* fix(bedrock/agents): message regeneration
* refactor: dynamic form elements using react-hook-form Controllers
* fix: agent icons/labels for messages
* fix: agent actions
* fix: use of new dynamic tags causing application crash
* refactor: dynamic settings touch-ups
* refactor: update Slider component to allow custom track class name
* refactor: update DynamicSlider component styles
* refactor: use Constants value for GLOBAL_PROJECT_NAME (enum)
* feat: agent share global methods/controllers
* fix: agents query
* fix: `getResponseModel`
* fix: share prompt a11y issue
* refactor: update SharePrompt dialog theme styles
* refactor: explicit typing for SharePrompt
* feat: add agent roles/permissions
* chore: update @librechat/agents dependency to version 1.4.7 for tool_call_ids edge case
* fix(Anthropic): messages.X.content.Y.tool_use.input: Input should be a valid dictionary
* fix: handle text parts with tool_call_ids and empty text
* fix: role initialization
* refactor: don't make instructions required
* refactor: improve typing of Text part
* fix: setShowStopButton for agents route
* chore: remove params for now
* fix: add streamBuffer and streamRate to help prevent 'Overloaded' errors from Anthropic API
* refactor: remove console.log statement in ContentRender component
* chore: typing, rename Context to Delete Button
* chore(DeleteButton): logging
* refactor(Action): make accessible
* style(Action): improve a11y again
* refactor: remove use/mention of mongoose sessions
* feat: first pass, sharing agents
* feat: visual indicator for global agent, remove author when serving to non-author
* wip: params
* chore: fix typing issues
* fix(schemas): typing
* refactor: improve accessibility of ListCard component and fix console React warning
* wip: reset templates for non-legacy new convos
* Revert "wip: params"
This reverts commit f8067e91d4adf7be9e0d9e914aaae79ac4689b80.
* Revert "refactor: dynamic form elements using react-hook-form Controllers"
This reverts commit 2150c4815d8c74a978a4b697aa8f54dc11e035d7.
* fix(Parameters): types and parameter effect update to only update local state to parameters
* refactor: optimize useDebouncedInput hook for better performance
* feat: first pass, anthropic bedrock params
* chore: paramEndpoints check for endpointType too
* fix: maxTokens to use coerceNumber.optional(),
* feat: extra chat model params
* chore: reduce code repetition
* refactor: improve preset title handling in SaveAsPresetDialog component
* refactor: improve preset handling in HeaderOptions component
* chore: improve typing, replace legacy dialog for SaveAsPresetDialog
* feat: save as preset from parameters panel
* fix: multi-search in select dropdown when using Option type
* refactor: update default showDefault value to false in Dynamic components
* feat: Bedrock presets settings
* chore: config, fix agents schema, update config version
* refactor: update AWS region variable name in bedrock options endpoint to BEDROCK_AWS_DEFAULT_REGION
* refactor: update baseEndpointSchema in config.ts to include baseURL property
* refactor: update createRun function to include req parameter and set streamRate based on provider
* feat: availableRegions via config
* refactor: remove unused demo agent controller file
* WIP: title
* Update @librechat/agents to version 1.5.0
* chore: addTitle.js to handle empty responseText
* feat: support images and titles
* feat: context token updates
* Refactor BaseClient test to use expect.objectContaining
* refactor: add model select, remove header options params, move side panel params below prompts
* chore: update models list, catch title error
* feat: model service for bedrock models (env)
* chore: Remove verbose debug log in AgentClient class following stream
* feat(bedrock): track token spend; fix: token rates, value key mapping for AWS models
* refactor: handle streamRate in `handleLLMNewToken` callback
* chore: AWS Bedrock example config in `.env.example`
* refactor: Rename bedrockMeta to bedrockGeneral in settings.ts and use for AI21 and Amazon Bedrock providers
* refactor: Update `.env.example` with AWS Bedrock model IDs URL and additional notes
* feat: titleModel support for bedrock
* refactor: Update `.env.example` with additional notes for AWS Bedrock model IDs
2024-09-09 12:06:59 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should not modify existing permissions for existing roles', async () => {
|
|
|
|
|
const customUserRole = {
|
|
|
|
|
name: SystemRoles.USER,
|
2025-04-05 01:47:14 +02:00
|
|
|
permissions: {
|
|
|
|
|
[PermissionTypes.PROMPTS]: {
|
|
|
|
|
[Permissions.USE]: false,
|
|
|
|
|
[Permissions.CREATE]: true,
|
2026-01-10 14:02:56 -05:00
|
|
|
[Permissions.SHARE]: true,
|
2025-04-05 01:47:14 +02:00
|
|
|
},
|
|
|
|
|
[PermissionTypes.BOOKMARKS]: { [Permissions.USE]: false },
|
🪨 feat: AWS Bedrock support (#3935)
* feat: Add BedrockIcon component to SVG library
* feat: EModelEndpoint.bedrock
* feat: first pass, bedrock chat. note: AgentClient is returning `agents` as conversation.endpoint
* fix: declare endpoint in initialization step
* chore: Update @librechat/agents dependency to version 1.4.5
* feat: backend content aggregation for agents/bedrock
* feat: abort agent requests
* feat: AWS Bedrock icons
* WIP: agent provider schema parsing
* chore: Update EditIcon props type
* refactor(useGenerationsByLatest): make agents and bedrock editable
* refactor: non-assistant message content, parts
* fix: Bedrock response `sender`
* fix: use endpointOption.model_parameters not endpointOption.modelOptions
* fix: types for step handler
* refactor: Update Agents.ToolCallDelta type
* refactor: Remove unnecessary assignment of parentMessageId in AskController
* refactor: remove unnecessary assignment of parentMessageId (agent request handler)
* fix(bedrock/agents): message regeneration
* refactor: dynamic form elements using react-hook-form Controllers
* fix: agent icons/labels for messages
* fix: agent actions
* fix: use of new dynamic tags causing application crash
* refactor: dynamic settings touch-ups
* refactor: update Slider component to allow custom track class name
* refactor: update DynamicSlider component styles
* refactor: use Constants value for GLOBAL_PROJECT_NAME (enum)
* feat: agent share global methods/controllers
* fix: agents query
* fix: `getResponseModel`
* fix: share prompt a11y issue
* refactor: update SharePrompt dialog theme styles
* refactor: explicit typing for SharePrompt
* feat: add agent roles/permissions
* chore: update @librechat/agents dependency to version 1.4.7 for tool_call_ids edge case
* fix(Anthropic): messages.X.content.Y.tool_use.input: Input should be a valid dictionary
* fix: handle text parts with tool_call_ids and empty text
* fix: role initialization
* refactor: don't make instructions required
* refactor: improve typing of Text part
* fix: setShowStopButton for agents route
* chore: remove params for now
* fix: add streamBuffer and streamRate to help prevent 'Overloaded' errors from Anthropic API
* refactor: remove console.log statement in ContentRender component
* chore: typing, rename Context to Delete Button
* chore(DeleteButton): logging
* refactor(Action): make accessible
* style(Action): improve a11y again
* refactor: remove use/mention of mongoose sessions
* feat: first pass, sharing agents
* feat: visual indicator for global agent, remove author when serving to non-author
* wip: params
* chore: fix typing issues
* fix(schemas): typing
* refactor: improve accessibility of ListCard component and fix console React warning
* wip: reset templates for non-legacy new convos
* Revert "wip: params"
This reverts commit f8067e91d4adf7be9e0d9e914aaae79ac4689b80.
* Revert "refactor: dynamic form elements using react-hook-form Controllers"
This reverts commit 2150c4815d8c74a978a4b697aa8f54dc11e035d7.
* fix(Parameters): types and parameter effect update to only update local state to parameters
* refactor: optimize useDebouncedInput hook for better performance
* feat: first pass, anthropic bedrock params
* chore: paramEndpoints check for endpointType too
* fix: maxTokens to use coerceNumber.optional(),
* feat: extra chat model params
* chore: reduce code repetition
* refactor: improve preset title handling in SaveAsPresetDialog component
* refactor: improve preset handling in HeaderOptions component
* chore: improve typing, replace legacy dialog for SaveAsPresetDialog
* feat: save as preset from parameters panel
* fix: multi-search in select dropdown when using Option type
* refactor: update default showDefault value to false in Dynamic components
* feat: Bedrock presets settings
* chore: config, fix agents schema, update config version
* refactor: update AWS region variable name in bedrock options endpoint to BEDROCK_AWS_DEFAULT_REGION
* refactor: update baseEndpointSchema in config.ts to include baseURL property
* refactor: update createRun function to include req parameter and set streamRate based on provider
* feat: availableRegions via config
* refactor: remove unused demo agent controller file
* WIP: title
* Update @librechat/agents to version 1.5.0
* chore: addTitle.js to handle empty responseText
* feat: support images and titles
* feat: context token updates
* Refactor BaseClient test to use expect.objectContaining
* refactor: add model select, remove header options params, move side panel params below prompts
* chore: update models list, catch title error
* feat: model service for bedrock models (env)
* chore: Remove verbose debug log in AgentClient class following stream
* feat(bedrock): track token spend; fix: token rates, value key mapping for AWS models
* refactor: handle streamRate in `handleLLMNewToken` callback
* chore: AWS Bedrock example config in `.env.example`
* refactor: Rename bedrockMeta to bedrockGeneral in settings.ts and use for AI21 and Amazon Bedrock providers
* refactor: Update `.env.example` with AWS Bedrock model IDs URL and additional notes
* feat: titleModel support for bedrock
* refactor: Update `.env.example` with additional notes for AWS Bedrock model IDs
2024-09-09 12:06:59 -04:00
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
await new Role(customUserRole).save();
|
|
|
|
|
await initializeRoles();
|
|
|
|
|
|
2025-04-05 01:47:14 +02:00
|
|
|
const userRole = await getRoleByName(SystemRoles.USER);
|
|
|
|
|
expect(userRole.permissions[PermissionTypes.PROMPTS]).toEqual(
|
|
|
|
|
customUserRole.permissions[PermissionTypes.PROMPTS],
|
|
|
|
|
);
|
|
|
|
|
expect(userRole.permissions[PermissionTypes.BOOKMARKS]).toEqual(
|
|
|
|
|
customUserRole.permissions[PermissionTypes.BOOKMARKS],
|
|
|
|
|
);
|
|
|
|
|
expect(userRole.permissions[PermissionTypes.AGENTS]).toBeDefined();
|
🪨 feat: AWS Bedrock support (#3935)
* feat: Add BedrockIcon component to SVG library
* feat: EModelEndpoint.bedrock
* feat: first pass, bedrock chat. note: AgentClient is returning `agents` as conversation.endpoint
* fix: declare endpoint in initialization step
* chore: Update @librechat/agents dependency to version 1.4.5
* feat: backend content aggregation for agents/bedrock
* feat: abort agent requests
* feat: AWS Bedrock icons
* WIP: agent provider schema parsing
* chore: Update EditIcon props type
* refactor(useGenerationsByLatest): make agents and bedrock editable
* refactor: non-assistant message content, parts
* fix: Bedrock response `sender`
* fix: use endpointOption.model_parameters not endpointOption.modelOptions
* fix: types for step handler
* refactor: Update Agents.ToolCallDelta type
* refactor: Remove unnecessary assignment of parentMessageId in AskController
* refactor: remove unnecessary assignment of parentMessageId (agent request handler)
* fix(bedrock/agents): message regeneration
* refactor: dynamic form elements using react-hook-form Controllers
* fix: agent icons/labels for messages
* fix: agent actions
* fix: use of new dynamic tags causing application crash
* refactor: dynamic settings touch-ups
* refactor: update Slider component to allow custom track class name
* refactor: update DynamicSlider component styles
* refactor: use Constants value for GLOBAL_PROJECT_NAME (enum)
* feat: agent share global methods/controllers
* fix: agents query
* fix: `getResponseModel`
* fix: share prompt a11y issue
* refactor: update SharePrompt dialog theme styles
* refactor: explicit typing for SharePrompt
* feat: add agent roles/permissions
* chore: update @librechat/agents dependency to version 1.4.7 for tool_call_ids edge case
* fix(Anthropic): messages.X.content.Y.tool_use.input: Input should be a valid dictionary
* fix: handle text parts with tool_call_ids and empty text
* fix: role initialization
* refactor: don't make instructions required
* refactor: improve typing of Text part
* fix: setShowStopButton for agents route
* chore: remove params for now
* fix: add streamBuffer and streamRate to help prevent 'Overloaded' errors from Anthropic API
* refactor: remove console.log statement in ContentRender component
* chore: typing, rename Context to Delete Button
* chore(DeleteButton): logging
* refactor(Action): make accessible
* style(Action): improve a11y again
* refactor: remove use/mention of mongoose sessions
* feat: first pass, sharing agents
* feat: visual indicator for global agent, remove author when serving to non-author
* wip: params
* chore: fix typing issues
* fix(schemas): typing
* refactor: improve accessibility of ListCard component and fix console React warning
* wip: reset templates for non-legacy new convos
* Revert "wip: params"
This reverts commit f8067e91d4adf7be9e0d9e914aaae79ac4689b80.
* Revert "refactor: dynamic form elements using react-hook-form Controllers"
This reverts commit 2150c4815d8c74a978a4b697aa8f54dc11e035d7.
* fix(Parameters): types and parameter effect update to only update local state to parameters
* refactor: optimize useDebouncedInput hook for better performance
* feat: first pass, anthropic bedrock params
* chore: paramEndpoints check for endpointType too
* fix: maxTokens to use coerceNumber.optional(),
* feat: extra chat model params
* chore: reduce code repetition
* refactor: improve preset title handling in SaveAsPresetDialog component
* refactor: improve preset handling in HeaderOptions component
* chore: improve typing, replace legacy dialog for SaveAsPresetDialog
* feat: save as preset from parameters panel
* fix: multi-search in select dropdown when using Option type
* refactor: update default showDefault value to false in Dynamic components
* feat: Bedrock presets settings
* chore: config, fix agents schema, update config version
* refactor: update AWS region variable name in bedrock options endpoint to BEDROCK_AWS_DEFAULT_REGION
* refactor: update baseEndpointSchema in config.ts to include baseURL property
* refactor: update createRun function to include req parameter and set streamRate based on provider
* feat: availableRegions via config
* refactor: remove unused demo agent controller file
* WIP: title
* Update @librechat/agents to version 1.5.0
* chore: addTitle.js to handle empty responseText
* feat: support images and titles
* feat: context token updates
* Refactor BaseClient test to use expect.objectContaining
* refactor: add model select, remove header options params, move side panel params below prompts
* chore: update models list, catch title error
* feat: model service for bedrock models (env)
* chore: Remove verbose debug log in AgentClient class following stream
* feat(bedrock): track token spend; fix: token rates, value key mapping for AWS models
* refactor: handle streamRate in `handleLLMNewToken` callback
* chore: AWS Bedrock example config in `.env.example`
* refactor: Rename bedrockMeta to bedrockGeneral in settings.ts and use for AI21 and Amazon Bedrock providers
* refactor: Update `.env.example` with AWS Bedrock model IDs URL and additional notes
* feat: titleModel support for bedrock
* refactor: Update `.env.example` with additional notes for AWS Bedrock model IDs
2024-09-09 12:06:59 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should add new permission types to existing roles', async () => {
|
|
|
|
|
const partialUserRole = {
|
|
|
|
|
name: SystemRoles.USER,
|
2025-04-05 01:47:14 +02:00
|
|
|
permissions: {
|
|
|
|
|
[PermissionTypes.PROMPTS]:
|
|
|
|
|
roleDefaults[SystemRoles.USER].permissions[PermissionTypes.PROMPTS],
|
|
|
|
|
[PermissionTypes.BOOKMARKS]:
|
|
|
|
|
roleDefaults[SystemRoles.USER].permissions[PermissionTypes.BOOKMARKS],
|
|
|
|
|
},
|
🪨 feat: AWS Bedrock support (#3935)
* feat: Add BedrockIcon component to SVG library
* feat: EModelEndpoint.bedrock
* feat: first pass, bedrock chat. note: AgentClient is returning `agents` as conversation.endpoint
* fix: declare endpoint in initialization step
* chore: Update @librechat/agents dependency to version 1.4.5
* feat: backend content aggregation for agents/bedrock
* feat: abort agent requests
* feat: AWS Bedrock icons
* WIP: agent provider schema parsing
* chore: Update EditIcon props type
* refactor(useGenerationsByLatest): make agents and bedrock editable
* refactor: non-assistant message content, parts
* fix: Bedrock response `sender`
* fix: use endpointOption.model_parameters not endpointOption.modelOptions
* fix: types for step handler
* refactor: Update Agents.ToolCallDelta type
* refactor: Remove unnecessary assignment of parentMessageId in AskController
* refactor: remove unnecessary assignment of parentMessageId (agent request handler)
* fix(bedrock/agents): message regeneration
* refactor: dynamic form elements using react-hook-form Controllers
* fix: agent icons/labels for messages
* fix: agent actions
* fix: use of new dynamic tags causing application crash
* refactor: dynamic settings touch-ups
* refactor: update Slider component to allow custom track class name
* refactor: update DynamicSlider component styles
* refactor: use Constants value for GLOBAL_PROJECT_NAME (enum)
* feat: agent share global methods/controllers
* fix: agents query
* fix: `getResponseModel`
* fix: share prompt a11y issue
* refactor: update SharePrompt dialog theme styles
* refactor: explicit typing for SharePrompt
* feat: add agent roles/permissions
* chore: update @librechat/agents dependency to version 1.4.7 for tool_call_ids edge case
* fix(Anthropic): messages.X.content.Y.tool_use.input: Input should be a valid dictionary
* fix: handle text parts with tool_call_ids and empty text
* fix: role initialization
* refactor: don't make instructions required
* refactor: improve typing of Text part
* fix: setShowStopButton for agents route
* chore: remove params for now
* fix: add streamBuffer and streamRate to help prevent 'Overloaded' errors from Anthropic API
* refactor: remove console.log statement in ContentRender component
* chore: typing, rename Context to Delete Button
* chore(DeleteButton): logging
* refactor(Action): make accessible
* style(Action): improve a11y again
* refactor: remove use/mention of mongoose sessions
* feat: first pass, sharing agents
* feat: visual indicator for global agent, remove author when serving to non-author
* wip: params
* chore: fix typing issues
* fix(schemas): typing
* refactor: improve accessibility of ListCard component and fix console React warning
* wip: reset templates for non-legacy new convos
* Revert "wip: params"
This reverts commit f8067e91d4adf7be9e0d9e914aaae79ac4689b80.
* Revert "refactor: dynamic form elements using react-hook-form Controllers"
This reverts commit 2150c4815d8c74a978a4b697aa8f54dc11e035d7.
* fix(Parameters): types and parameter effect update to only update local state to parameters
* refactor: optimize useDebouncedInput hook for better performance
* feat: first pass, anthropic bedrock params
* chore: paramEndpoints check for endpointType too
* fix: maxTokens to use coerceNumber.optional(),
* feat: extra chat model params
* chore: reduce code repetition
* refactor: improve preset title handling in SaveAsPresetDialog component
* refactor: improve preset handling in HeaderOptions component
* chore: improve typing, replace legacy dialog for SaveAsPresetDialog
* feat: save as preset from parameters panel
* fix: multi-search in select dropdown when using Option type
* refactor: update default showDefault value to false in Dynamic components
* feat: Bedrock presets settings
* chore: config, fix agents schema, update config version
* refactor: update AWS region variable name in bedrock options endpoint to BEDROCK_AWS_DEFAULT_REGION
* refactor: update baseEndpointSchema in config.ts to include baseURL property
* refactor: update createRun function to include req parameter and set streamRate based on provider
* feat: availableRegions via config
* refactor: remove unused demo agent controller file
* WIP: title
* Update @librechat/agents to version 1.5.0
* chore: addTitle.js to handle empty responseText
* feat: support images and titles
* feat: context token updates
* Refactor BaseClient test to use expect.objectContaining
* refactor: add model select, remove header options params, move side panel params below prompts
* chore: update models list, catch title error
* feat: model service for bedrock models (env)
* chore: Remove verbose debug log in AgentClient class following stream
* feat(bedrock): track token spend; fix: token rates, value key mapping for AWS models
* refactor: handle streamRate in `handleLLMNewToken` callback
* chore: AWS Bedrock example config in `.env.example`
* refactor: Rename bedrockMeta to bedrockGeneral in settings.ts and use for AI21 and Amazon Bedrock providers
* refactor: Update `.env.example` with AWS Bedrock model IDs URL and additional notes
* feat: titleModel support for bedrock
* refactor: Update `.env.example` with additional notes for AWS Bedrock model IDs
2024-09-09 12:06:59 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
await new Role(partialUserRole).save();
|
|
|
|
|
await initializeRoles();
|
|
|
|
|
|
2025-04-05 01:47:14 +02:00
|
|
|
const userRole = await getRoleByName(SystemRoles.USER);
|
|
|
|
|
expect(userRole.permissions[PermissionTypes.AGENTS]).toBeDefined();
|
📦 refactor: Consolidate DB models, encapsulating Mongoose usage in `data-schemas` (#11830)
* chore: move database model methods to /packages/data-schemas
* chore: add TypeScript ESLint rule to warn on unused variables
* refactor: model imports to streamline access
- Consolidated model imports across various files to improve code organization and reduce redundancy.
- Updated imports for models such as Assistant, Message, Conversation, and others to a unified import path.
- Adjusted middleware and service files to reflect the new import structure, ensuring functionality remains intact.
- Enhanced test files to align with the new import paths, maintaining test coverage and integrity.
* chore: migrate database models to packages/data-schemas and refactor all direct Mongoose Model usage outside of data-schemas
* test: update agent model mocks in unit tests
- Added `getAgent` mock to `client.test.js` to enhance test coverage for agent-related functionality.
- Removed redundant `getAgent` and `getAgents` mocks from `openai.spec.js` and `responses.unit.spec.js` to streamline test setup and reduce duplication.
- Ensured consistency in agent mock implementations across test files.
* fix: update types in data-schemas
* refactor: enhance type definitions in transaction and spending methods
- Updated type definitions in `checkBalance.ts` to use specific request and response types.
- Refined `spendTokens.ts` to utilize a new `SpendTxData` interface for better clarity and type safety.
- Improved transaction handling in `transaction.ts` by introducing `TransactionResult` and `TxData` interfaces, ensuring consistent data structures across methods.
- Adjusted unit tests in `transaction.spec.ts` to accommodate new type definitions and enhance robustness.
* refactor: streamline model imports and enhance code organization
- Consolidated model imports across various controllers and services to a unified import path, improving code clarity and reducing redundancy.
- Updated multiple files to reflect the new import structure, ensuring all functionalities remain intact.
- Enhanced overall code organization by removing duplicate import statements and optimizing the usage of model methods.
* feat: implement loadAddedAgent and refactor agent loading logic
- Introduced `loadAddedAgent` function to handle loading agents from added conversations, supporting multi-convo parallel execution.
- Created a new `load.ts` file to encapsulate agent loading functionalities, including `loadEphemeralAgent` and `loadAgent`.
- Updated the `index.ts` file to export the new `load` module instead of the deprecated `loadAgent`.
- Enhanced type definitions and improved error handling in the agent loading process.
- Adjusted unit tests to reflect changes in the agent loading structure and ensure comprehensive coverage.
* refactor: enhance balance handling with new update interface
- Introduced `IBalanceUpdate` interface to streamline balance update operations across the codebase.
- Updated `upsertBalanceFields` method signatures in `balance.ts`, `transaction.ts`, and related tests to utilize the new interface for improved type safety.
- Adjusted type imports in `balance.spec.ts` to include `IBalanceUpdate`, ensuring consistency in balance management functionalities.
- Enhanced overall code clarity and maintainability by refining type definitions related to balance operations.
* feat: add unit tests for loadAgent functionality and enhance agent loading logic
- Introduced comprehensive unit tests for the `loadAgent` function, covering various scenarios including null and empty agent IDs, loading of ephemeral agents, and permission checks.
- Enhanced the `initializeClient` function by moving `getConvoFiles` to the correct position in the database method exports, ensuring proper functionality.
- Improved test coverage for agent loading, including handling of non-existent agents and user permissions.
* chore: reorder memory method exports for consistency
- Moved `deleteAllUserMemories` to the correct position in the exported memory methods, ensuring a consistent and logical order of method exports in `memory.ts`.
2026-02-17 18:23:44 -05:00
|
|
|
expect(userRole.permissions[PermissionTypes.AGENTS]?.CREATE).toBeDefined();
|
|
|
|
|
expect(userRole.permissions[PermissionTypes.AGENTS]?.USE).toBeDefined();
|
|
|
|
|
expect(userRole.permissions[PermissionTypes.AGENTS]?.SHARE).toBeDefined();
|
🪨 feat: AWS Bedrock support (#3935)
* feat: Add BedrockIcon component to SVG library
* feat: EModelEndpoint.bedrock
* feat: first pass, bedrock chat. note: AgentClient is returning `agents` as conversation.endpoint
* fix: declare endpoint in initialization step
* chore: Update @librechat/agents dependency to version 1.4.5
* feat: backend content aggregation for agents/bedrock
* feat: abort agent requests
* feat: AWS Bedrock icons
* WIP: agent provider schema parsing
* chore: Update EditIcon props type
* refactor(useGenerationsByLatest): make agents and bedrock editable
* refactor: non-assistant message content, parts
* fix: Bedrock response `sender`
* fix: use endpointOption.model_parameters not endpointOption.modelOptions
* fix: types for step handler
* refactor: Update Agents.ToolCallDelta type
* refactor: Remove unnecessary assignment of parentMessageId in AskController
* refactor: remove unnecessary assignment of parentMessageId (agent request handler)
* fix(bedrock/agents): message regeneration
* refactor: dynamic form elements using react-hook-form Controllers
* fix: agent icons/labels for messages
* fix: agent actions
* fix: use of new dynamic tags causing application crash
* refactor: dynamic settings touch-ups
* refactor: update Slider component to allow custom track class name
* refactor: update DynamicSlider component styles
* refactor: use Constants value for GLOBAL_PROJECT_NAME (enum)
* feat: agent share global methods/controllers
* fix: agents query
* fix: `getResponseModel`
* fix: share prompt a11y issue
* refactor: update SharePrompt dialog theme styles
* refactor: explicit typing for SharePrompt
* feat: add agent roles/permissions
* chore: update @librechat/agents dependency to version 1.4.7 for tool_call_ids edge case
* fix(Anthropic): messages.X.content.Y.tool_use.input: Input should be a valid dictionary
* fix: handle text parts with tool_call_ids and empty text
* fix: role initialization
* refactor: don't make instructions required
* refactor: improve typing of Text part
* fix: setShowStopButton for agents route
* chore: remove params for now
* fix: add streamBuffer and streamRate to help prevent 'Overloaded' errors from Anthropic API
* refactor: remove console.log statement in ContentRender component
* chore: typing, rename Context to Delete Button
* chore(DeleteButton): logging
* refactor(Action): make accessible
* style(Action): improve a11y again
* refactor: remove use/mention of mongoose sessions
* feat: first pass, sharing agents
* feat: visual indicator for global agent, remove author when serving to non-author
* wip: params
* chore: fix typing issues
* fix(schemas): typing
* refactor: improve accessibility of ListCard component and fix console React warning
* wip: reset templates for non-legacy new convos
* Revert "wip: params"
This reverts commit f8067e91d4adf7be9e0d9e914aaae79ac4689b80.
* Revert "refactor: dynamic form elements using react-hook-form Controllers"
This reverts commit 2150c4815d8c74a978a4b697aa8f54dc11e035d7.
* fix(Parameters): types and parameter effect update to only update local state to parameters
* refactor: optimize useDebouncedInput hook for better performance
* feat: first pass, anthropic bedrock params
* chore: paramEndpoints check for endpointType too
* fix: maxTokens to use coerceNumber.optional(),
* feat: extra chat model params
* chore: reduce code repetition
* refactor: improve preset title handling in SaveAsPresetDialog component
* refactor: improve preset handling in HeaderOptions component
* chore: improve typing, replace legacy dialog for SaveAsPresetDialog
* feat: save as preset from parameters panel
* fix: multi-search in select dropdown when using Option type
* refactor: update default showDefault value to false in Dynamic components
* feat: Bedrock presets settings
* chore: config, fix agents schema, update config version
* refactor: update AWS region variable name in bedrock options endpoint to BEDROCK_AWS_DEFAULT_REGION
* refactor: update baseEndpointSchema in config.ts to include baseURL property
* refactor: update createRun function to include req parameter and set streamRate based on provider
* feat: availableRegions via config
* refactor: remove unused demo agent controller file
* WIP: title
* Update @librechat/agents to version 1.5.0
* chore: addTitle.js to handle empty responseText
* feat: support images and titles
* feat: context token updates
* Refactor BaseClient test to use expect.objectContaining
* refactor: add model select, remove header options params, move side panel params below prompts
* chore: update models list, catch title error
* feat: model service for bedrock models (env)
* chore: Remove verbose debug log in AgentClient class following stream
* feat(bedrock): track token spend; fix: token rates, value key mapping for AWS models
* refactor: handle streamRate in `handleLLMNewToken` callback
* chore: AWS Bedrock example config in `.env.example`
* refactor: Rename bedrockMeta to bedrockGeneral in settings.ts and use for AI21 and Amazon Bedrock providers
* refactor: Update `.env.example` with AWS Bedrock model IDs URL and additional notes
* feat: titleModel support for bedrock
* refactor: Update `.env.example` with additional notes for AWS Bedrock model IDs
2024-09-09 12:06:59 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should handle multiple runs without duplicating or modifying data', async () => {
|
|
|
|
|
await initializeRoles();
|
|
|
|
|
await initializeRoles();
|
|
|
|
|
|
|
|
|
|
const adminRoles = await Role.find({ name: SystemRoles.ADMIN });
|
|
|
|
|
const userRoles = await Role.find({ name: SystemRoles.USER });
|
|
|
|
|
|
|
|
|
|
expect(adminRoles).toHaveLength(1);
|
|
|
|
|
expect(userRoles).toHaveLength(1);
|
|
|
|
|
|
📦 refactor: Consolidate DB models, encapsulating Mongoose usage in `data-schemas` (#11830)
* chore: move database model methods to /packages/data-schemas
* chore: add TypeScript ESLint rule to warn on unused variables
* refactor: model imports to streamline access
- Consolidated model imports across various files to improve code organization and reduce redundancy.
- Updated imports for models such as Assistant, Message, Conversation, and others to a unified import path.
- Adjusted middleware and service files to reflect the new import structure, ensuring functionality remains intact.
- Enhanced test files to align with the new import paths, maintaining test coverage and integrity.
* chore: migrate database models to packages/data-schemas and refactor all direct Mongoose Model usage outside of data-schemas
* test: update agent model mocks in unit tests
- Added `getAgent` mock to `client.test.js` to enhance test coverage for agent-related functionality.
- Removed redundant `getAgent` and `getAgents` mocks from `openai.spec.js` and `responses.unit.spec.js` to streamline test setup and reduce duplication.
- Ensured consistency in agent mock implementations across test files.
* fix: update types in data-schemas
* refactor: enhance type definitions in transaction and spending methods
- Updated type definitions in `checkBalance.ts` to use specific request and response types.
- Refined `spendTokens.ts` to utilize a new `SpendTxData` interface for better clarity and type safety.
- Improved transaction handling in `transaction.ts` by introducing `TransactionResult` and `TxData` interfaces, ensuring consistent data structures across methods.
- Adjusted unit tests in `transaction.spec.ts` to accommodate new type definitions and enhance robustness.
* refactor: streamline model imports and enhance code organization
- Consolidated model imports across various controllers and services to a unified import path, improving code clarity and reducing redundancy.
- Updated multiple files to reflect the new import structure, ensuring all functionalities remain intact.
- Enhanced overall code organization by removing duplicate import statements and optimizing the usage of model methods.
* feat: implement loadAddedAgent and refactor agent loading logic
- Introduced `loadAddedAgent` function to handle loading agents from added conversations, supporting multi-convo parallel execution.
- Created a new `load.ts` file to encapsulate agent loading functionalities, including `loadEphemeralAgent` and `loadAgent`.
- Updated the `index.ts` file to export the new `load` module instead of the deprecated `loadAgent`.
- Enhanced type definitions and improved error handling in the agent loading process.
- Adjusted unit tests to reflect changes in the agent loading structure and ensure comprehensive coverage.
* refactor: enhance balance handling with new update interface
- Introduced `IBalanceUpdate` interface to streamline balance update operations across the codebase.
- Updated `upsertBalanceFields` method signatures in `balance.ts`, `transaction.ts`, and related tests to utilize the new interface for improved type safety.
- Adjusted type imports in `balance.spec.ts` to include `IBalanceUpdate`, ensuring consistency in balance management functionalities.
- Enhanced overall code clarity and maintainability by refining type definitions related to balance operations.
* feat: add unit tests for loadAgent functionality and enhance agent loading logic
- Introduced comprehensive unit tests for the `loadAgent` function, covering various scenarios including null and empty agent IDs, loading of ephemeral agents, and permission checks.
- Enhanced the `initializeClient` function by moving `getConvoFiles` to the correct position in the database method exports, ensuring proper functionality.
- Improved test coverage for agent loading, including handling of non-existent agents and user permissions.
* chore: reorder memory method exports for consistency
- Moved `deleteAllUserMemories` to the correct position in the exported memory methods, ensuring a consistent and logical order of method exports in `memory.ts`.
2026-02-17 18:23:44 -05:00
|
|
|
const adminPerms = adminRoles[0].toObject().permissions as RolePermissions;
|
|
|
|
|
const userPerms = userRoles[0].toObject().permissions as RolePermissions;
|
🪨 feat: AWS Bedrock support (#3935)
* feat: Add BedrockIcon component to SVG library
* feat: EModelEndpoint.bedrock
* feat: first pass, bedrock chat. note: AgentClient is returning `agents` as conversation.endpoint
* fix: declare endpoint in initialization step
* chore: Update @librechat/agents dependency to version 1.4.5
* feat: backend content aggregation for agents/bedrock
* feat: abort agent requests
* feat: AWS Bedrock icons
* WIP: agent provider schema parsing
* chore: Update EditIcon props type
* refactor(useGenerationsByLatest): make agents and bedrock editable
* refactor: non-assistant message content, parts
* fix: Bedrock response `sender`
* fix: use endpointOption.model_parameters not endpointOption.modelOptions
* fix: types for step handler
* refactor: Update Agents.ToolCallDelta type
* refactor: Remove unnecessary assignment of parentMessageId in AskController
* refactor: remove unnecessary assignment of parentMessageId (agent request handler)
* fix(bedrock/agents): message regeneration
* refactor: dynamic form elements using react-hook-form Controllers
* fix: agent icons/labels for messages
* fix: agent actions
* fix: use of new dynamic tags causing application crash
* refactor: dynamic settings touch-ups
* refactor: update Slider component to allow custom track class name
* refactor: update DynamicSlider component styles
* refactor: use Constants value for GLOBAL_PROJECT_NAME (enum)
* feat: agent share global methods/controllers
* fix: agents query
* fix: `getResponseModel`
* fix: share prompt a11y issue
* refactor: update SharePrompt dialog theme styles
* refactor: explicit typing for SharePrompt
* feat: add agent roles/permissions
* chore: update @librechat/agents dependency to version 1.4.7 for tool_call_ids edge case
* fix(Anthropic): messages.X.content.Y.tool_use.input: Input should be a valid dictionary
* fix: handle text parts with tool_call_ids and empty text
* fix: role initialization
* refactor: don't make instructions required
* refactor: improve typing of Text part
* fix: setShowStopButton for agents route
* chore: remove params for now
* fix: add streamBuffer and streamRate to help prevent 'Overloaded' errors from Anthropic API
* refactor: remove console.log statement in ContentRender component
* chore: typing, rename Context to Delete Button
* chore(DeleteButton): logging
* refactor(Action): make accessible
* style(Action): improve a11y again
* refactor: remove use/mention of mongoose sessions
* feat: first pass, sharing agents
* feat: visual indicator for global agent, remove author when serving to non-author
* wip: params
* chore: fix typing issues
* fix(schemas): typing
* refactor: improve accessibility of ListCard component and fix console React warning
* wip: reset templates for non-legacy new convos
* Revert "wip: params"
This reverts commit f8067e91d4adf7be9e0d9e914aaae79ac4689b80.
* Revert "refactor: dynamic form elements using react-hook-form Controllers"
This reverts commit 2150c4815d8c74a978a4b697aa8f54dc11e035d7.
* fix(Parameters): types and parameter effect update to only update local state to parameters
* refactor: optimize useDebouncedInput hook for better performance
* feat: first pass, anthropic bedrock params
* chore: paramEndpoints check for endpointType too
* fix: maxTokens to use coerceNumber.optional(),
* feat: extra chat model params
* chore: reduce code repetition
* refactor: improve preset title handling in SaveAsPresetDialog component
* refactor: improve preset handling in HeaderOptions component
* chore: improve typing, replace legacy dialog for SaveAsPresetDialog
* feat: save as preset from parameters panel
* fix: multi-search in select dropdown when using Option type
* refactor: update default showDefault value to false in Dynamic components
* feat: Bedrock presets settings
* chore: config, fix agents schema, update config version
* refactor: update AWS region variable name in bedrock options endpoint to BEDROCK_AWS_DEFAULT_REGION
* refactor: update baseEndpointSchema in config.ts to include baseURL property
* refactor: update createRun function to include req parameter and set streamRate based on provider
* feat: availableRegions via config
* refactor: remove unused demo agent controller file
* WIP: title
* Update @librechat/agents to version 1.5.0
* chore: addTitle.js to handle empty responseText
* feat: support images and titles
* feat: context token updates
* Refactor BaseClient test to use expect.objectContaining
* refactor: add model select, remove header options params, move side panel params below prompts
* chore: update models list, catch title error
* feat: model service for bedrock models (env)
* chore: Remove verbose debug log in AgentClient class following stream
* feat(bedrock): track token spend; fix: token rates, value key mapping for AWS models
* refactor: handle streamRate in `handleLLMNewToken` callback
* chore: AWS Bedrock example config in `.env.example`
* refactor: Rename bedrockMeta to bedrockGeneral in settings.ts and use for AI21 and Amazon Bedrock providers
* refactor: Update `.env.example` with AWS Bedrock model IDs URL and additional notes
* feat: titleModel support for bedrock
* refactor: Update `.env.example` with additional notes for AWS Bedrock model IDs
2024-09-09 12:06:59 -04:00
|
|
|
Object.values(PermissionTypes).forEach((permType) => {
|
2025-04-05 01:47:14 +02:00
|
|
|
expect(adminPerms[permType]).toBeDefined();
|
|
|
|
|
expect(userPerms[permType]).toBeDefined();
|
🪨 feat: AWS Bedrock support (#3935)
* feat: Add BedrockIcon component to SVG library
* feat: EModelEndpoint.bedrock
* feat: first pass, bedrock chat. note: AgentClient is returning `agents` as conversation.endpoint
* fix: declare endpoint in initialization step
* chore: Update @librechat/agents dependency to version 1.4.5
* feat: backend content aggregation for agents/bedrock
* feat: abort agent requests
* feat: AWS Bedrock icons
* WIP: agent provider schema parsing
* chore: Update EditIcon props type
* refactor(useGenerationsByLatest): make agents and bedrock editable
* refactor: non-assistant message content, parts
* fix: Bedrock response `sender`
* fix: use endpointOption.model_parameters not endpointOption.modelOptions
* fix: types for step handler
* refactor: Update Agents.ToolCallDelta type
* refactor: Remove unnecessary assignment of parentMessageId in AskController
* refactor: remove unnecessary assignment of parentMessageId (agent request handler)
* fix(bedrock/agents): message regeneration
* refactor: dynamic form elements using react-hook-form Controllers
* fix: agent icons/labels for messages
* fix: agent actions
* fix: use of new dynamic tags causing application crash
* refactor: dynamic settings touch-ups
* refactor: update Slider component to allow custom track class name
* refactor: update DynamicSlider component styles
* refactor: use Constants value for GLOBAL_PROJECT_NAME (enum)
* feat: agent share global methods/controllers
* fix: agents query
* fix: `getResponseModel`
* fix: share prompt a11y issue
* refactor: update SharePrompt dialog theme styles
* refactor: explicit typing for SharePrompt
* feat: add agent roles/permissions
* chore: update @librechat/agents dependency to version 1.4.7 for tool_call_ids edge case
* fix(Anthropic): messages.X.content.Y.tool_use.input: Input should be a valid dictionary
* fix: handle text parts with tool_call_ids and empty text
* fix: role initialization
* refactor: don't make instructions required
* refactor: improve typing of Text part
* fix: setShowStopButton for agents route
* chore: remove params for now
* fix: add streamBuffer and streamRate to help prevent 'Overloaded' errors from Anthropic API
* refactor: remove console.log statement in ContentRender component
* chore: typing, rename Context to Delete Button
* chore(DeleteButton): logging
* refactor(Action): make accessible
* style(Action): improve a11y again
* refactor: remove use/mention of mongoose sessions
* feat: first pass, sharing agents
* feat: visual indicator for global agent, remove author when serving to non-author
* wip: params
* chore: fix typing issues
* fix(schemas): typing
* refactor: improve accessibility of ListCard component and fix console React warning
* wip: reset templates for non-legacy new convos
* Revert "wip: params"
This reverts commit f8067e91d4adf7be9e0d9e914aaae79ac4689b80.
* Revert "refactor: dynamic form elements using react-hook-form Controllers"
This reverts commit 2150c4815d8c74a978a4b697aa8f54dc11e035d7.
* fix(Parameters): types and parameter effect update to only update local state to parameters
* refactor: optimize useDebouncedInput hook for better performance
* feat: first pass, anthropic bedrock params
* chore: paramEndpoints check for endpointType too
* fix: maxTokens to use coerceNumber.optional(),
* feat: extra chat model params
* chore: reduce code repetition
* refactor: improve preset title handling in SaveAsPresetDialog component
* refactor: improve preset handling in HeaderOptions component
* chore: improve typing, replace legacy dialog for SaveAsPresetDialog
* feat: save as preset from parameters panel
* fix: multi-search in select dropdown when using Option type
* refactor: update default showDefault value to false in Dynamic components
* feat: Bedrock presets settings
* chore: config, fix agents schema, update config version
* refactor: update AWS region variable name in bedrock options endpoint to BEDROCK_AWS_DEFAULT_REGION
* refactor: update baseEndpointSchema in config.ts to include baseURL property
* refactor: update createRun function to include req parameter and set streamRate based on provider
* feat: availableRegions via config
* refactor: remove unused demo agent controller file
* WIP: title
* Update @librechat/agents to version 1.5.0
* chore: addTitle.js to handle empty responseText
* feat: support images and titles
* feat: context token updates
* Refactor BaseClient test to use expect.objectContaining
* refactor: add model select, remove header options params, move side panel params below prompts
* chore: update models list, catch title error
* feat: model service for bedrock models (env)
* chore: Remove verbose debug log in AgentClient class following stream
* feat(bedrock): track token spend; fix: token rates, value key mapping for AWS models
* refactor: handle streamRate in `handleLLMNewToken` callback
* chore: AWS Bedrock example config in `.env.example`
* refactor: Rename bedrockMeta to bedrockGeneral in settings.ts and use for AI21 and Amazon Bedrock providers
* refactor: Update `.env.example` with AWS Bedrock model IDs URL and additional notes
* feat: titleModel support for bedrock
* refactor: Update `.env.example` with additional notes for AWS Bedrock model IDs
2024-09-09 12:06:59 -04:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should update roles with missing permission types from roleDefaults', async () => {
|
|
|
|
|
const partialAdminRole = {
|
|
|
|
|
name: SystemRoles.ADMIN,
|
2025-04-05 01:47:14 +02:00
|
|
|
permissions: {
|
|
|
|
|
[PermissionTypes.PROMPTS]: {
|
|
|
|
|
[Permissions.USE]: false,
|
|
|
|
|
[Permissions.CREATE]: false,
|
2026-01-10 14:02:56 -05:00
|
|
|
[Permissions.SHARE]: false,
|
2025-04-05 01:47:14 +02:00
|
|
|
},
|
|
|
|
|
[PermissionTypes.BOOKMARKS]:
|
|
|
|
|
roleDefaults[SystemRoles.ADMIN].permissions[PermissionTypes.BOOKMARKS],
|
🪨 feat: AWS Bedrock support (#3935)
* feat: Add BedrockIcon component to SVG library
* feat: EModelEndpoint.bedrock
* feat: first pass, bedrock chat. note: AgentClient is returning `agents` as conversation.endpoint
* fix: declare endpoint in initialization step
* chore: Update @librechat/agents dependency to version 1.4.5
* feat: backend content aggregation for agents/bedrock
* feat: abort agent requests
* feat: AWS Bedrock icons
* WIP: agent provider schema parsing
* chore: Update EditIcon props type
* refactor(useGenerationsByLatest): make agents and bedrock editable
* refactor: non-assistant message content, parts
* fix: Bedrock response `sender`
* fix: use endpointOption.model_parameters not endpointOption.modelOptions
* fix: types for step handler
* refactor: Update Agents.ToolCallDelta type
* refactor: Remove unnecessary assignment of parentMessageId in AskController
* refactor: remove unnecessary assignment of parentMessageId (agent request handler)
* fix(bedrock/agents): message regeneration
* refactor: dynamic form elements using react-hook-form Controllers
* fix: agent icons/labels for messages
* fix: agent actions
* fix: use of new dynamic tags causing application crash
* refactor: dynamic settings touch-ups
* refactor: update Slider component to allow custom track class name
* refactor: update DynamicSlider component styles
* refactor: use Constants value for GLOBAL_PROJECT_NAME (enum)
* feat: agent share global methods/controllers
* fix: agents query
* fix: `getResponseModel`
* fix: share prompt a11y issue
* refactor: update SharePrompt dialog theme styles
* refactor: explicit typing for SharePrompt
* feat: add agent roles/permissions
* chore: update @librechat/agents dependency to version 1.4.7 for tool_call_ids edge case
* fix(Anthropic): messages.X.content.Y.tool_use.input: Input should be a valid dictionary
* fix: handle text parts with tool_call_ids and empty text
* fix: role initialization
* refactor: don't make instructions required
* refactor: improve typing of Text part
* fix: setShowStopButton for agents route
* chore: remove params for now
* fix: add streamBuffer and streamRate to help prevent 'Overloaded' errors from Anthropic API
* refactor: remove console.log statement in ContentRender component
* chore: typing, rename Context to Delete Button
* chore(DeleteButton): logging
* refactor(Action): make accessible
* style(Action): improve a11y again
* refactor: remove use/mention of mongoose sessions
* feat: first pass, sharing agents
* feat: visual indicator for global agent, remove author when serving to non-author
* wip: params
* chore: fix typing issues
* fix(schemas): typing
* refactor: improve accessibility of ListCard component and fix console React warning
* wip: reset templates for non-legacy new convos
* Revert "wip: params"
This reverts commit f8067e91d4adf7be9e0d9e914aaae79ac4689b80.
* Revert "refactor: dynamic form elements using react-hook-form Controllers"
This reverts commit 2150c4815d8c74a978a4b697aa8f54dc11e035d7.
* fix(Parameters): types and parameter effect update to only update local state to parameters
* refactor: optimize useDebouncedInput hook for better performance
* feat: first pass, anthropic bedrock params
* chore: paramEndpoints check for endpointType too
* fix: maxTokens to use coerceNumber.optional(),
* feat: extra chat model params
* chore: reduce code repetition
* refactor: improve preset title handling in SaveAsPresetDialog component
* refactor: improve preset handling in HeaderOptions component
* chore: improve typing, replace legacy dialog for SaveAsPresetDialog
* feat: save as preset from parameters panel
* fix: multi-search in select dropdown when using Option type
* refactor: update default showDefault value to false in Dynamic components
* feat: Bedrock presets settings
* chore: config, fix agents schema, update config version
* refactor: update AWS region variable name in bedrock options endpoint to BEDROCK_AWS_DEFAULT_REGION
* refactor: update baseEndpointSchema in config.ts to include baseURL property
* refactor: update createRun function to include req parameter and set streamRate based on provider
* feat: availableRegions via config
* refactor: remove unused demo agent controller file
* WIP: title
* Update @librechat/agents to version 1.5.0
* chore: addTitle.js to handle empty responseText
* feat: support images and titles
* feat: context token updates
* Refactor BaseClient test to use expect.objectContaining
* refactor: add model select, remove header options params, move side panel params below prompts
* chore: update models list, catch title error
* feat: model service for bedrock models (env)
* chore: Remove verbose debug log in AgentClient class following stream
* feat(bedrock): track token spend; fix: token rates, value key mapping for AWS models
* refactor: handle streamRate in `handleLLMNewToken` callback
* chore: AWS Bedrock example config in `.env.example`
* refactor: Rename bedrockMeta to bedrockGeneral in settings.ts and use for AI21 and Amazon Bedrock providers
* refactor: Update `.env.example` with AWS Bedrock model IDs URL and additional notes
* feat: titleModel support for bedrock
* refactor: Update `.env.example` with additional notes for AWS Bedrock model IDs
2024-09-09 12:06:59 -04:00
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
await new Role(partialAdminRole).save();
|
|
|
|
|
await initializeRoles();
|
|
|
|
|
|
2025-04-05 01:47:14 +02:00
|
|
|
const adminRole = await getRoleByName(SystemRoles.ADMIN);
|
|
|
|
|
expect(adminRole.permissions[PermissionTypes.PROMPTS]).toEqual(
|
|
|
|
|
partialAdminRole.permissions[PermissionTypes.PROMPTS],
|
|
|
|
|
);
|
|
|
|
|
expect(adminRole.permissions[PermissionTypes.AGENTS]).toBeDefined();
|
📦 refactor: Consolidate DB models, encapsulating Mongoose usage in `data-schemas` (#11830)
* chore: move database model methods to /packages/data-schemas
* chore: add TypeScript ESLint rule to warn on unused variables
* refactor: model imports to streamline access
- Consolidated model imports across various files to improve code organization and reduce redundancy.
- Updated imports for models such as Assistant, Message, Conversation, and others to a unified import path.
- Adjusted middleware and service files to reflect the new import structure, ensuring functionality remains intact.
- Enhanced test files to align with the new import paths, maintaining test coverage and integrity.
* chore: migrate database models to packages/data-schemas and refactor all direct Mongoose Model usage outside of data-schemas
* test: update agent model mocks in unit tests
- Added `getAgent` mock to `client.test.js` to enhance test coverage for agent-related functionality.
- Removed redundant `getAgent` and `getAgents` mocks from `openai.spec.js` and `responses.unit.spec.js` to streamline test setup and reduce duplication.
- Ensured consistency in agent mock implementations across test files.
* fix: update types in data-schemas
* refactor: enhance type definitions in transaction and spending methods
- Updated type definitions in `checkBalance.ts` to use specific request and response types.
- Refined `spendTokens.ts` to utilize a new `SpendTxData` interface for better clarity and type safety.
- Improved transaction handling in `transaction.ts` by introducing `TransactionResult` and `TxData` interfaces, ensuring consistent data structures across methods.
- Adjusted unit tests in `transaction.spec.ts` to accommodate new type definitions and enhance robustness.
* refactor: streamline model imports and enhance code organization
- Consolidated model imports across various controllers and services to a unified import path, improving code clarity and reducing redundancy.
- Updated multiple files to reflect the new import structure, ensuring all functionalities remain intact.
- Enhanced overall code organization by removing duplicate import statements and optimizing the usage of model methods.
* feat: implement loadAddedAgent and refactor agent loading logic
- Introduced `loadAddedAgent` function to handle loading agents from added conversations, supporting multi-convo parallel execution.
- Created a new `load.ts` file to encapsulate agent loading functionalities, including `loadEphemeralAgent` and `loadAgent`.
- Updated the `index.ts` file to export the new `load` module instead of the deprecated `loadAgent`.
- Enhanced type definitions and improved error handling in the agent loading process.
- Adjusted unit tests to reflect changes in the agent loading structure and ensure comprehensive coverage.
* refactor: enhance balance handling with new update interface
- Introduced `IBalanceUpdate` interface to streamline balance update operations across the codebase.
- Updated `upsertBalanceFields` method signatures in `balance.ts`, `transaction.ts`, and related tests to utilize the new interface for improved type safety.
- Adjusted type imports in `balance.spec.ts` to include `IBalanceUpdate`, ensuring consistency in balance management functionalities.
- Enhanced overall code clarity and maintainability by refining type definitions related to balance operations.
* feat: add unit tests for loadAgent functionality and enhance agent loading logic
- Introduced comprehensive unit tests for the `loadAgent` function, covering various scenarios including null and empty agent IDs, loading of ephemeral agents, and permission checks.
- Enhanced the `initializeClient` function by moving `getConvoFiles` to the correct position in the database method exports, ensuring proper functionality.
- Improved test coverage for agent loading, including handling of non-existent agents and user permissions.
* chore: reorder memory method exports for consistency
- Moved `deleteAllUserMemories` to the correct position in the exported memory methods, ensuring a consistent and logical order of method exports in `memory.ts`.
2026-02-17 18:23:44 -05:00
|
|
|
expect(adminRole.permissions[PermissionTypes.AGENTS]?.CREATE).toBeDefined();
|
|
|
|
|
expect(adminRole.permissions[PermissionTypes.AGENTS]?.USE).toBeDefined();
|
|
|
|
|
expect(adminRole.permissions[PermissionTypes.AGENTS]?.SHARE).toBeDefined();
|
🪨 feat: AWS Bedrock support (#3935)
* feat: Add BedrockIcon component to SVG library
* feat: EModelEndpoint.bedrock
* feat: first pass, bedrock chat. note: AgentClient is returning `agents` as conversation.endpoint
* fix: declare endpoint in initialization step
* chore: Update @librechat/agents dependency to version 1.4.5
* feat: backend content aggregation for agents/bedrock
* feat: abort agent requests
* feat: AWS Bedrock icons
* WIP: agent provider schema parsing
* chore: Update EditIcon props type
* refactor(useGenerationsByLatest): make agents and bedrock editable
* refactor: non-assistant message content, parts
* fix: Bedrock response `sender`
* fix: use endpointOption.model_parameters not endpointOption.modelOptions
* fix: types for step handler
* refactor: Update Agents.ToolCallDelta type
* refactor: Remove unnecessary assignment of parentMessageId in AskController
* refactor: remove unnecessary assignment of parentMessageId (agent request handler)
* fix(bedrock/agents): message regeneration
* refactor: dynamic form elements using react-hook-form Controllers
* fix: agent icons/labels for messages
* fix: agent actions
* fix: use of new dynamic tags causing application crash
* refactor: dynamic settings touch-ups
* refactor: update Slider component to allow custom track class name
* refactor: update DynamicSlider component styles
* refactor: use Constants value for GLOBAL_PROJECT_NAME (enum)
* feat: agent share global methods/controllers
* fix: agents query
* fix: `getResponseModel`
* fix: share prompt a11y issue
* refactor: update SharePrompt dialog theme styles
* refactor: explicit typing for SharePrompt
* feat: add agent roles/permissions
* chore: update @librechat/agents dependency to version 1.4.7 for tool_call_ids edge case
* fix(Anthropic): messages.X.content.Y.tool_use.input: Input should be a valid dictionary
* fix: handle text parts with tool_call_ids and empty text
* fix: role initialization
* refactor: don't make instructions required
* refactor: improve typing of Text part
* fix: setShowStopButton for agents route
* chore: remove params for now
* fix: add streamBuffer and streamRate to help prevent 'Overloaded' errors from Anthropic API
* refactor: remove console.log statement in ContentRender component
* chore: typing, rename Context to Delete Button
* chore(DeleteButton): logging
* refactor(Action): make accessible
* style(Action): improve a11y again
* refactor: remove use/mention of mongoose sessions
* feat: first pass, sharing agents
* feat: visual indicator for global agent, remove author when serving to non-author
* wip: params
* chore: fix typing issues
* fix(schemas): typing
* refactor: improve accessibility of ListCard component and fix console React warning
* wip: reset templates for non-legacy new convos
* Revert "wip: params"
This reverts commit f8067e91d4adf7be9e0d9e914aaae79ac4689b80.
* Revert "refactor: dynamic form elements using react-hook-form Controllers"
This reverts commit 2150c4815d8c74a978a4b697aa8f54dc11e035d7.
* fix(Parameters): types and parameter effect update to only update local state to parameters
* refactor: optimize useDebouncedInput hook for better performance
* feat: first pass, anthropic bedrock params
* chore: paramEndpoints check for endpointType too
* fix: maxTokens to use coerceNumber.optional(),
* feat: extra chat model params
* chore: reduce code repetition
* refactor: improve preset title handling in SaveAsPresetDialog component
* refactor: improve preset handling in HeaderOptions component
* chore: improve typing, replace legacy dialog for SaveAsPresetDialog
* feat: save as preset from parameters panel
* fix: multi-search in select dropdown when using Option type
* refactor: update default showDefault value to false in Dynamic components
* feat: Bedrock presets settings
* chore: config, fix agents schema, update config version
* refactor: update AWS region variable name in bedrock options endpoint to BEDROCK_AWS_DEFAULT_REGION
* refactor: update baseEndpointSchema in config.ts to include baseURL property
* refactor: update createRun function to include req parameter and set streamRate based on provider
* feat: availableRegions via config
* refactor: remove unused demo agent controller file
* WIP: title
* Update @librechat/agents to version 1.5.0
* chore: addTitle.js to handle empty responseText
* feat: support images and titles
* feat: context token updates
* Refactor BaseClient test to use expect.objectContaining
* refactor: add model select, remove header options params, move side panel params below prompts
* chore: update models list, catch title error
* feat: model service for bedrock models (env)
* chore: Remove verbose debug log in AgentClient class following stream
* feat(bedrock): track token spend; fix: token rates, value key mapping for AWS models
* refactor: handle streamRate in `handleLLMNewToken` callback
* chore: AWS Bedrock example config in `.env.example`
* refactor: Rename bedrockMeta to bedrockGeneral in settings.ts and use for AI21 and Amazon Bedrock providers
* refactor: Update `.env.example` with AWS Bedrock model IDs URL and additional notes
* feat: titleModel support for bedrock
* refactor: Update `.env.example` with additional notes for AWS Bedrock model IDs
2024-09-09 12:06:59 -04:00
|
|
|
});
|
2024-09-09 16:29:24 -04:00
|
|
|
|
|
|
|
|
it('should include MULTI_CONVO permissions when creating default roles', async () => {
|
|
|
|
|
await initializeRoles();
|
|
|
|
|
|
2025-04-05 01:47:14 +02:00
|
|
|
const adminRole = await getRoleByName(SystemRoles.ADMIN);
|
|
|
|
|
const userRole = await getRoleByName(SystemRoles.USER);
|
2024-09-09 16:29:24 -04:00
|
|
|
|
2025-04-05 01:47:14 +02:00
|
|
|
expect(adminRole.permissions[PermissionTypes.MULTI_CONVO]).toBeDefined();
|
|
|
|
|
expect(userRole.permissions[PermissionTypes.MULTI_CONVO]).toBeDefined();
|
📦 refactor: Consolidate DB models, encapsulating Mongoose usage in `data-schemas` (#11830)
* chore: move database model methods to /packages/data-schemas
* chore: add TypeScript ESLint rule to warn on unused variables
* refactor: model imports to streamline access
- Consolidated model imports across various files to improve code organization and reduce redundancy.
- Updated imports for models such as Assistant, Message, Conversation, and others to a unified import path.
- Adjusted middleware and service files to reflect the new import structure, ensuring functionality remains intact.
- Enhanced test files to align with the new import paths, maintaining test coverage and integrity.
* chore: migrate database models to packages/data-schemas and refactor all direct Mongoose Model usage outside of data-schemas
* test: update agent model mocks in unit tests
- Added `getAgent` mock to `client.test.js` to enhance test coverage for agent-related functionality.
- Removed redundant `getAgent` and `getAgents` mocks from `openai.spec.js` and `responses.unit.spec.js` to streamline test setup and reduce duplication.
- Ensured consistency in agent mock implementations across test files.
* fix: update types in data-schemas
* refactor: enhance type definitions in transaction and spending methods
- Updated type definitions in `checkBalance.ts` to use specific request and response types.
- Refined `spendTokens.ts` to utilize a new `SpendTxData` interface for better clarity and type safety.
- Improved transaction handling in `transaction.ts` by introducing `TransactionResult` and `TxData` interfaces, ensuring consistent data structures across methods.
- Adjusted unit tests in `transaction.spec.ts` to accommodate new type definitions and enhance robustness.
* refactor: streamline model imports and enhance code organization
- Consolidated model imports across various controllers and services to a unified import path, improving code clarity and reducing redundancy.
- Updated multiple files to reflect the new import structure, ensuring all functionalities remain intact.
- Enhanced overall code organization by removing duplicate import statements and optimizing the usage of model methods.
* feat: implement loadAddedAgent and refactor agent loading logic
- Introduced `loadAddedAgent` function to handle loading agents from added conversations, supporting multi-convo parallel execution.
- Created a new `load.ts` file to encapsulate agent loading functionalities, including `loadEphemeralAgent` and `loadAgent`.
- Updated the `index.ts` file to export the new `load` module instead of the deprecated `loadAgent`.
- Enhanced type definitions and improved error handling in the agent loading process.
- Adjusted unit tests to reflect changes in the agent loading structure and ensure comprehensive coverage.
* refactor: enhance balance handling with new update interface
- Introduced `IBalanceUpdate` interface to streamline balance update operations across the codebase.
- Updated `upsertBalanceFields` method signatures in `balance.ts`, `transaction.ts`, and related tests to utilize the new interface for improved type safety.
- Adjusted type imports in `balance.spec.ts` to include `IBalanceUpdate`, ensuring consistency in balance management functionalities.
- Enhanced overall code clarity and maintainability by refining type definitions related to balance operations.
* feat: add unit tests for loadAgent functionality and enhance agent loading logic
- Introduced comprehensive unit tests for the `loadAgent` function, covering various scenarios including null and empty agent IDs, loading of ephemeral agents, and permission checks.
- Enhanced the `initializeClient` function by moving `getConvoFiles` to the correct position in the database method exports, ensuring proper functionality.
- Improved test coverage for agent loading, including handling of non-existent agents and user permissions.
* chore: reorder memory method exports for consistency
- Moved `deleteAllUserMemories` to the correct position in the exported memory methods, ensuring a consistent and logical order of method exports in `memory.ts`.
2026-02-17 18:23:44 -05:00
|
|
|
expect(adminRole.permissions[PermissionTypes.MULTI_CONVO]?.USE).toBe(
|
2025-04-05 01:47:14 +02:00
|
|
|
roleDefaults[SystemRoles.ADMIN].permissions[PermissionTypes.MULTI_CONVO].USE,
|
2024-09-09 16:29:24 -04:00
|
|
|
);
|
📦 refactor: Consolidate DB models, encapsulating Mongoose usage in `data-schemas` (#11830)
* chore: move database model methods to /packages/data-schemas
* chore: add TypeScript ESLint rule to warn on unused variables
* refactor: model imports to streamline access
- Consolidated model imports across various files to improve code organization and reduce redundancy.
- Updated imports for models such as Assistant, Message, Conversation, and others to a unified import path.
- Adjusted middleware and service files to reflect the new import structure, ensuring functionality remains intact.
- Enhanced test files to align with the new import paths, maintaining test coverage and integrity.
* chore: migrate database models to packages/data-schemas and refactor all direct Mongoose Model usage outside of data-schemas
* test: update agent model mocks in unit tests
- Added `getAgent` mock to `client.test.js` to enhance test coverage for agent-related functionality.
- Removed redundant `getAgent` and `getAgents` mocks from `openai.spec.js` and `responses.unit.spec.js` to streamline test setup and reduce duplication.
- Ensured consistency in agent mock implementations across test files.
* fix: update types in data-schemas
* refactor: enhance type definitions in transaction and spending methods
- Updated type definitions in `checkBalance.ts` to use specific request and response types.
- Refined `spendTokens.ts` to utilize a new `SpendTxData` interface for better clarity and type safety.
- Improved transaction handling in `transaction.ts` by introducing `TransactionResult` and `TxData` interfaces, ensuring consistent data structures across methods.
- Adjusted unit tests in `transaction.spec.ts` to accommodate new type definitions and enhance robustness.
* refactor: streamline model imports and enhance code organization
- Consolidated model imports across various controllers and services to a unified import path, improving code clarity and reducing redundancy.
- Updated multiple files to reflect the new import structure, ensuring all functionalities remain intact.
- Enhanced overall code organization by removing duplicate import statements and optimizing the usage of model methods.
* feat: implement loadAddedAgent and refactor agent loading logic
- Introduced `loadAddedAgent` function to handle loading agents from added conversations, supporting multi-convo parallel execution.
- Created a new `load.ts` file to encapsulate agent loading functionalities, including `loadEphemeralAgent` and `loadAgent`.
- Updated the `index.ts` file to export the new `load` module instead of the deprecated `loadAgent`.
- Enhanced type definitions and improved error handling in the agent loading process.
- Adjusted unit tests to reflect changes in the agent loading structure and ensure comprehensive coverage.
* refactor: enhance balance handling with new update interface
- Introduced `IBalanceUpdate` interface to streamline balance update operations across the codebase.
- Updated `upsertBalanceFields` method signatures in `balance.ts`, `transaction.ts`, and related tests to utilize the new interface for improved type safety.
- Adjusted type imports in `balance.spec.ts` to include `IBalanceUpdate`, ensuring consistency in balance management functionalities.
- Enhanced overall code clarity and maintainability by refining type definitions related to balance operations.
* feat: add unit tests for loadAgent functionality and enhance agent loading logic
- Introduced comprehensive unit tests for the `loadAgent` function, covering various scenarios including null and empty agent IDs, loading of ephemeral agents, and permission checks.
- Enhanced the `initializeClient` function by moving `getConvoFiles` to the correct position in the database method exports, ensuring proper functionality.
- Improved test coverage for agent loading, including handling of non-existent agents and user permissions.
* chore: reorder memory method exports for consistency
- Moved `deleteAllUserMemories` to the correct position in the exported memory methods, ensuring a consistent and logical order of method exports in `memory.ts`.
2026-02-17 18:23:44 -05:00
|
|
|
expect(userRole.permissions[PermissionTypes.MULTI_CONVO]?.USE).toBe(
|
2025-04-05 01:47:14 +02:00
|
|
|
roleDefaults[SystemRoles.USER].permissions[PermissionTypes.MULTI_CONVO].USE,
|
2024-09-09 16:29:24 -04:00
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should add MULTI_CONVO permissions to existing roles without them', async () => {
|
|
|
|
|
const partialUserRole = {
|
|
|
|
|
name: SystemRoles.USER,
|
2025-04-05 01:47:14 +02:00
|
|
|
permissions: {
|
|
|
|
|
[PermissionTypes.PROMPTS]:
|
|
|
|
|
roleDefaults[SystemRoles.USER].permissions[PermissionTypes.PROMPTS],
|
|
|
|
|
[PermissionTypes.BOOKMARKS]:
|
|
|
|
|
roleDefaults[SystemRoles.USER].permissions[PermissionTypes.BOOKMARKS],
|
|
|
|
|
},
|
2024-09-09 16:29:24 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
await new Role(partialUserRole).save();
|
|
|
|
|
await initializeRoles();
|
|
|
|
|
|
2025-04-05 01:47:14 +02:00
|
|
|
const userRole = await getRoleByName(SystemRoles.USER);
|
|
|
|
|
expect(userRole.permissions[PermissionTypes.MULTI_CONVO]).toBeDefined();
|
📦 refactor: Consolidate DB models, encapsulating Mongoose usage in `data-schemas` (#11830)
* chore: move database model methods to /packages/data-schemas
* chore: add TypeScript ESLint rule to warn on unused variables
* refactor: model imports to streamline access
- Consolidated model imports across various files to improve code organization and reduce redundancy.
- Updated imports for models such as Assistant, Message, Conversation, and others to a unified import path.
- Adjusted middleware and service files to reflect the new import structure, ensuring functionality remains intact.
- Enhanced test files to align with the new import paths, maintaining test coverage and integrity.
* chore: migrate database models to packages/data-schemas and refactor all direct Mongoose Model usage outside of data-schemas
* test: update agent model mocks in unit tests
- Added `getAgent` mock to `client.test.js` to enhance test coverage for agent-related functionality.
- Removed redundant `getAgent` and `getAgents` mocks from `openai.spec.js` and `responses.unit.spec.js` to streamline test setup and reduce duplication.
- Ensured consistency in agent mock implementations across test files.
* fix: update types in data-schemas
* refactor: enhance type definitions in transaction and spending methods
- Updated type definitions in `checkBalance.ts` to use specific request and response types.
- Refined `spendTokens.ts` to utilize a new `SpendTxData` interface for better clarity and type safety.
- Improved transaction handling in `transaction.ts` by introducing `TransactionResult` and `TxData` interfaces, ensuring consistent data structures across methods.
- Adjusted unit tests in `transaction.spec.ts` to accommodate new type definitions and enhance robustness.
* refactor: streamline model imports and enhance code organization
- Consolidated model imports across various controllers and services to a unified import path, improving code clarity and reducing redundancy.
- Updated multiple files to reflect the new import structure, ensuring all functionalities remain intact.
- Enhanced overall code organization by removing duplicate import statements and optimizing the usage of model methods.
* feat: implement loadAddedAgent and refactor agent loading logic
- Introduced `loadAddedAgent` function to handle loading agents from added conversations, supporting multi-convo parallel execution.
- Created a new `load.ts` file to encapsulate agent loading functionalities, including `loadEphemeralAgent` and `loadAgent`.
- Updated the `index.ts` file to export the new `load` module instead of the deprecated `loadAgent`.
- Enhanced type definitions and improved error handling in the agent loading process.
- Adjusted unit tests to reflect changes in the agent loading structure and ensure comprehensive coverage.
* refactor: enhance balance handling with new update interface
- Introduced `IBalanceUpdate` interface to streamline balance update operations across the codebase.
- Updated `upsertBalanceFields` method signatures in `balance.ts`, `transaction.ts`, and related tests to utilize the new interface for improved type safety.
- Adjusted type imports in `balance.spec.ts` to include `IBalanceUpdate`, ensuring consistency in balance management functionalities.
- Enhanced overall code clarity and maintainability by refining type definitions related to balance operations.
* feat: add unit tests for loadAgent functionality and enhance agent loading logic
- Introduced comprehensive unit tests for the `loadAgent` function, covering various scenarios including null and empty agent IDs, loading of ephemeral agents, and permission checks.
- Enhanced the `initializeClient` function by moving `getConvoFiles` to the correct position in the database method exports, ensuring proper functionality.
- Improved test coverage for agent loading, including handling of non-existent agents and user permissions.
* chore: reorder memory method exports for consistency
- Moved `deleteAllUserMemories` to the correct position in the exported memory methods, ensuring a consistent and logical order of method exports in `memory.ts`.
2026-02-17 18:23:44 -05:00
|
|
|
expect(userRole.permissions[PermissionTypes.MULTI_CONVO]?.USE).toBeDefined();
|
2024-09-09 16:29:24 -04:00
|
|
|
});
|
🪨 feat: AWS Bedrock support (#3935)
* feat: Add BedrockIcon component to SVG library
* feat: EModelEndpoint.bedrock
* feat: first pass, bedrock chat. note: AgentClient is returning `agents` as conversation.endpoint
* fix: declare endpoint in initialization step
* chore: Update @librechat/agents dependency to version 1.4.5
* feat: backend content aggregation for agents/bedrock
* feat: abort agent requests
* feat: AWS Bedrock icons
* WIP: agent provider schema parsing
* chore: Update EditIcon props type
* refactor(useGenerationsByLatest): make agents and bedrock editable
* refactor: non-assistant message content, parts
* fix: Bedrock response `sender`
* fix: use endpointOption.model_parameters not endpointOption.modelOptions
* fix: types for step handler
* refactor: Update Agents.ToolCallDelta type
* refactor: Remove unnecessary assignment of parentMessageId in AskController
* refactor: remove unnecessary assignment of parentMessageId (agent request handler)
* fix(bedrock/agents): message regeneration
* refactor: dynamic form elements using react-hook-form Controllers
* fix: agent icons/labels for messages
* fix: agent actions
* fix: use of new dynamic tags causing application crash
* refactor: dynamic settings touch-ups
* refactor: update Slider component to allow custom track class name
* refactor: update DynamicSlider component styles
* refactor: use Constants value for GLOBAL_PROJECT_NAME (enum)
* feat: agent share global methods/controllers
* fix: agents query
* fix: `getResponseModel`
* fix: share prompt a11y issue
* refactor: update SharePrompt dialog theme styles
* refactor: explicit typing for SharePrompt
* feat: add agent roles/permissions
* chore: update @librechat/agents dependency to version 1.4.7 for tool_call_ids edge case
* fix(Anthropic): messages.X.content.Y.tool_use.input: Input should be a valid dictionary
* fix: handle text parts with tool_call_ids and empty text
* fix: role initialization
* refactor: don't make instructions required
* refactor: improve typing of Text part
* fix: setShowStopButton for agents route
* chore: remove params for now
* fix: add streamBuffer and streamRate to help prevent 'Overloaded' errors from Anthropic API
* refactor: remove console.log statement in ContentRender component
* chore: typing, rename Context to Delete Button
* chore(DeleteButton): logging
* refactor(Action): make accessible
* style(Action): improve a11y again
* refactor: remove use/mention of mongoose sessions
* feat: first pass, sharing agents
* feat: visual indicator for global agent, remove author when serving to non-author
* wip: params
* chore: fix typing issues
* fix(schemas): typing
* refactor: improve accessibility of ListCard component and fix console React warning
* wip: reset templates for non-legacy new convos
* Revert "wip: params"
This reverts commit f8067e91d4adf7be9e0d9e914aaae79ac4689b80.
* Revert "refactor: dynamic form elements using react-hook-form Controllers"
This reverts commit 2150c4815d8c74a978a4b697aa8f54dc11e035d7.
* fix(Parameters): types and parameter effect update to only update local state to parameters
* refactor: optimize useDebouncedInput hook for better performance
* feat: first pass, anthropic bedrock params
* chore: paramEndpoints check for endpointType too
* fix: maxTokens to use coerceNumber.optional(),
* feat: extra chat model params
* chore: reduce code repetition
* refactor: improve preset title handling in SaveAsPresetDialog component
* refactor: improve preset handling in HeaderOptions component
* chore: improve typing, replace legacy dialog for SaveAsPresetDialog
* feat: save as preset from parameters panel
* fix: multi-search in select dropdown when using Option type
* refactor: update default showDefault value to false in Dynamic components
* feat: Bedrock presets settings
* chore: config, fix agents schema, update config version
* refactor: update AWS region variable name in bedrock options endpoint to BEDROCK_AWS_DEFAULT_REGION
* refactor: update baseEndpointSchema in config.ts to include baseURL property
* refactor: update createRun function to include req parameter and set streamRate based on provider
* feat: availableRegions via config
* refactor: remove unused demo agent controller file
* WIP: title
* Update @librechat/agents to version 1.5.0
* chore: addTitle.js to handle empty responseText
* feat: support images and titles
* feat: context token updates
* Refactor BaseClient test to use expect.objectContaining
* refactor: add model select, remove header options params, move side panel params below prompts
* chore: update models list, catch title error
* feat: model service for bedrock models (env)
* chore: Remove verbose debug log in AgentClient class following stream
* feat(bedrock): track token spend; fix: token rates, value key mapping for AWS models
* refactor: handle streamRate in `handleLLMNewToken` callback
* chore: AWS Bedrock example config in `.env.example`
* refactor: Rename bedrockMeta to bedrockGeneral in settings.ts and use for AI21 and Amazon Bedrock providers
* refactor: Update `.env.example` with AWS Bedrock model IDs URL and additional notes
* feat: titleModel support for bedrock
* refactor: Update `.env.example` with additional notes for AWS Bedrock model IDs
2024-09-09 12:06:59 -04:00
|
|
|
});
|
🪪 feat: Admin Roles API Endpoints (#12400)
* feat: add createRole and deleteRole methods to role
* feat: add admin roles handler factory and Express routes
* fix: address convention violations in admin roles handlers
* fix: rename createRole/deleteRole to avoid AccessRole name collision
The existing accessRole.ts already exports createRole/deleteRole for the
AccessRole model. In createMethods index.ts, these are spread after
roleMethods, overwriting them. Renamed our Role methods to
createRoleByName/deleteRoleByName to match the existing pattern
(getRoleByName, updateRoleByName) and avoid the collision.
* feat: add description field to Role model
- Add description to IRole, CreateRoleRequest, UpdateRoleRequest types
- Add description field to Mongoose roleSchema (default: '')
- Wire description through createRoleHandler and updateRoleHandler
- Include description in listRoles select clause so it appears in list
* fix: address Copilot review findings in admin roles handlers
* test: add unit tests for admin roles and groups handlers
* test: add data-layer tests for createRoleByName, deleteRoleByName, listUsersByRole
* fix: allow system role updates when name is unchanged
The updateRoleHandler guard rejected any request where body.name matched
a system role, even when the name was not being changed. This blocked
editing a system role's description. Compare against the URL param to
only reject actual renames to reserved names.
* fix: address external review findings for admin roles
- Block renaming system roles (ADMIN/USER) and add user migration on rename
- Add input validation: name max-length, trim on update, duplicate name check
- Replace fragile String.includes error matching with prefix-based classification
- Catch MongoDB 11000 duplicate key in createRoleByName
- Add pagination (limit/offset/total) to getRoleMembersHandler
- Reverse delete order in deleteRoleByName — reassign users before deletion
- Add role existence check in removeRoleMember; drop unused createdAt select
- Add Array.isArray guard for permissions input; use consistent ?? coalescing
- Fix import ordering per AGENTS.md conventions
- Type-cast mongoose.models.User as Model<IUser> for proper TS inference
- Add comprehensive tests: rename guards, pagination, validation, 500 paths
* fix: address re-review findings for admin roles
- Gate deleteRoleByName on existence check — skip user reassignment and
cache invalidation when role doesn't exist (fixes test mismatch)
- Reverse rename order: migrate users before renaming role so a migration
failure leaves the system in a consistent state
- Add .sort({ _id: 1 }) to listUsersByRole for deterministic pagination
- Import shared AdminMember type from data-schemas instead of local copy;
make joinedAt optional since neither groups nor roles populate it
- Change IRole.description from optional to required to match schema default
- Add data-layer tests for updateUsersByRole and countUsersByRole
- Add handler test verifying users-first rename ordering and migration
failure safety
* fix: add rollback on rename failure and update PR description
- Roll back user migration if updateRoleByName returns null during a
rename (race: role deleted between existence check and update)
- Add test verifying rollback calls updateUsersByRole in reverse
- Update PR #12400 description to reflect current test counts (56
handler tests, 40 data-layer tests) and safety features
* fix: rollback on rename throw, description validation, delete/DRY cleanup
- Hoist isRename/trimmedName above try block so catch can roll back user
migration when updateRoleByName throws (not just returns null)
- Add description type + max-length (2000) validation in create and update,
consistent with groups handler
- Remove redundant getRoleByName existence check in deleteRoleHandler —
use deleteRoleByName return value directly
- Skip no-op name write when body.name equals current name (use isRename)
- Extract getUserModel() accessor to DRY repeated Model<IUser> casts
- Use name.trim() consistently in createRoleByName error messages
- Add tests: rename-throw rollback, description validation (create+update),
update delete test mocks to match simplified handler
* fix: guard spurious rollback, harden createRole error path, validate before DB calls
- Add migrationRan flag to prevent rollback of user migration that never ran
- Return generic message on 500 in createRoleHandler, specific only for 409
- Move description validation before DB queries in updateRoleHandler
- Return existing role early when update body has no changes
- Wrap cache.set in createRoleByName with try/catch to prevent masking DB success
- Add JSDoc on 11000 catch explaining compound unique index
- Add tests: spurious rollback guard, empty update body, description validation
ordering, listUsersByRole pagination
* fix: validate permissions in create, RoleConflictError, rollback safety, cache consistency
- Add permissions type/array validation in createRoleHandler
- Introduce RoleConflictError class replacing fragile string-prefix matching
- Wrap rollback in !role null path with try/catch for correct 404 response
- Wrap deleteRoleByName cache.set in try/catch matching createRoleByName
- Narrow updateRoleHandler body type to { name?, description? }
- Add tests: non-string description in create, rollback failure logging,
permissions array rejection, description max-length assertion fix
* feat: prevent removing the last admin user
Add guard in removeRoleMember that checks countUsersByRole before
demoting an ADMIN user, returning 400 if they are the last one.
* fix: move interleaved export below imports, add await to countUsersByRole
* fix: paginate listRoles, null-guard permissions handler, fix export ordering
- Add limit/offset/total pagination to listRoles matching the groups pattern
- Add countRoles data-layer method
- Omit permissions from listRoles select (getRole returns full document)
- Null-guard re-fetched role in updateRolePermissionsHandler
- Move interleaved export below all imports in methods/index.ts
* fix: address review findings — race safety, validation DRY, type accuracy, test coverage
- Add post-write admin count verification in removeRoleMember to prevent
zero-admin race condition (TOCTOU → rollback if count hits 0)
- Make IRole.description optional; backfill in initializeRoles for
pre-existing roles that lack the field (.lean() bypasses defaults)
- Extract parsePagination, validateNameParam, validateRoleName, and
validateDescription helpers to eliminate duplicated validation
- Add validateNameParam guard to all 7 handlers reading req.params.name
- Catch 11000 in updateRoleByName and surface as 409 via RoleConflictError
- Add idempotent skip in addRoleMember when user already has target role
- Verify updateRolePermissions test asserts response body
- Add data-layer tests: listRoles sort/pagination/projection, countRoles,
and createRoleByName 11000 duplicate key race
* fix: defensive rollback in removeRoleMember, type/style cleanup, test coverage
- Wrap removeRoleMember post-write admin rollback in try/catch so a
transient DB failure cannot leave the system with zero administrators
- Replace double `as unknown[] as IRole[]` cast with `.lean<IRole[]>()`
- Type parsePagination param explicitly; extract DEFAULT/MAX page constants
- Preserve original error cause in updateRoleByName re-throw
- Add test for rollback failure path in removeRoleMember (returns 400)
- Add test for pre-existing roles missing description field (.lean())
* chore: bump @librechat/data-schemas to 0.0.47
* fix: stale cache on rename, extract renameRole helper, shared pagination, cleanup
- Fix updateRoleByName cache bug: invalidate old key and populate new key
when updates.name differs from roleName (prevents stale cache after rename)
- Extract renameRole helper to eliminate mutable outer-scope state flags
(isRename, trimmedName, migrationRan) in updateRoleHandler
- Unify system-role protection to 403 for both rename-from and rename-to
- Extract parsePagination to shared admin/pagination.ts; use in both
roles.ts and groups.ts
- Extract name.trim() to local const in createRoleByName (was called 5×)
- Remove redundant findOne pre-check in deleteRoleByName
- Replace getUserModel closure with local const declarations
- Remove redundant description ?? '' in createRoleHandler (schema default)
- Add doc comment on updateRolePermissionsHandler noting cache dependency
- Add data-layer tests for cache rename behavior (old key null, new key set)
* fix: harden role guards, add User.role index, validate names, improve tests
- Add index on User.role field for efficient member queries at scale
- Replace fragile SystemRoles key lookup with value-based Set check (6 sites)
- Elevate rename rollback failure logging to CRITICAL (matches removeRoleMember)
- Guard removeRoleMember against non-ADMIN system roles (403 for USER)
- Fix parsePagination limit=0 gotcha: use parseInt + NaN check instead of ||
- Add control character and reserved path segment validation to role names
- Simplify validateRoleName: remove redundant casts and dead conditions
- Add JSDoc to deleteRoleByName documenting non-atomic window
- Split mixed value+type import in methods/index.ts per AGENTS.md
- Add 9 new tests: permissions assertion, combined rename+desc, createRole
with permissions, pagination edge cases, control char/reserved name
rejection, system role removeRoleMember guard
* fix: exact-case reserved name check, consistent validation, cleaner createRole
- Remove .toLowerCase() from reserved name check so only exact matches
(members, permissions) are rejected, not legitimate names like "Members"
- Extract trimmed const in validateRoleName for consistent validation
- Add control char check to validateNameParam for parity with body validation
- Build createRole roleData conditionally to avoid passing description: undefined
- Expand deleteRoleByName JSDoc documenting self-healing design and no-op trade-off
* fix: scope rename rollback to only migrated users, prevent cross-role corruption
Capture user IDs before forward migration so the rollback path only
reverts users this request actually moved. Previously the rollback called
updateUsersByRole(newName, currentName) which would sweep all users with
the new role — including any independently assigned by a concurrent admin
request — causing silent cross-role data corruption.
Adds findUserIdsByRole and updateUsersRoleByIds to the data layer.
Extracts rollbackMigratedUsers helper to deduplicate rollback sites.
* fix: guard last admin in addRoleMember to prevent zero-admin lockout
Since each user has exactly one role, addRoleMember implicitly removes
the user from their current role. Without a guard, reassigning the sole
admin to a non-admin role leaves zero admins and locks out admin
management. Adds the same countUsersByRole check used in removeRoleMember.
* fix: wire findUserIdsByRole and updateUsersRoleByIds into roles route
The scoped rollback deps added in c89b5db were missing from the route
DI wiring, causing renameRole to call undefined and return a 500.
* fix: post-write admin guard in addRoleMember, compound role index, review cleanup
- Add post-write admin count check + rollback to addRoleMember to match
removeRoleMember's two-phase TOCTOU protection (prevents zero-admin via
concurrent requests)
- Replace single-field User.role index with compound { role: 1, tenantId: 1 }
to align with existing multi-tenant index pattern (email, OAuth IDs)
- Narrow listRoles dep return type to RoleListItem (projected fields only)
- Refactor validateDescription to early-return style per AGENTS.md
- Remove redundant double .lean() in updateRoleByName
- Document rename snapshot race window in renameRole JSDoc
- Document cache null-set behavior in deleteRoleByName
- Add routing-coupling comment on RESERVED_ROLE_NAMES
- Add test for addRoleMember post-write rollback
* fix: review cleanup — system-role guard, type safety, JSDoc accuracy, tests
- Add system-role guard to addRoleMember: block direct assignment to
non-ADMIN system roles (403), symmetric with removeRoleMember
- Fix RESERVED_ROLE_NAMES comment: explain semantic URL ambiguity, not
a routing conflict (Express resolves single vs multi-segment correctly)
- Replace _id: unknown with Types.ObjectId | string per AGENTS.md
- Narrow listRoles data-layer return type to Pick<IRole, 'name' | 'description'>
to match the actual .select() projection
- Move updateRoleHandler param check inside try/catch for consistency
- Include user IDs in all CRITICAL rollback failure logs for operator recovery
- Clarify deleteRoleByName JSDoc: replace "self-healing" with "idempotent",
document that recovery requires caller retry
- Add tests: system-role guard, promote non-admin to ADMIN,
findUserIdsByRole throw prevents migration
* fix: include _id in listRoles return type to match RoleListItem
Pick<IRole, 'name' | 'description'> omits _id, making it incompatible
with the handler dep's RoleListItem which requires _id.
* fix: case-insensitive system role guard, reject null permissions, check updateUser result
- System role name checks now use case-insensitive comparison via
toUpperCase() — prevents creating 'admin' or 'user' which would
collide with the legacy roles route that uppercases params
- Reject permissions: null in createRole (typeof null === 'object'
was bypassing the validation)
- Check updateUser return in addRoleMember — return 404 if the user
was deleted between the findUser and updateUser calls
* fix: check updateUser return in removeRoleMember for concurrent delete safety
---------
Co-authored-by: Danny Avila <danny@librechat.ai>
2026-03-27 12:44:47 -07:00
|
|
|
|
|
|
|
|
describe('createRoleByName', () => {
|
|
|
|
|
it('creates a custom role and caches it', async () => {
|
|
|
|
|
const role = await createRoleByName({ name: 'editor', description: 'Can edit' });
|
|
|
|
|
|
|
|
|
|
expect(role.name).toBe('editor');
|
|
|
|
|
expect(role.description).toBe('Can edit');
|
|
|
|
|
expect(mockCache.set).toHaveBeenCalledWith(
|
|
|
|
|
'editor',
|
|
|
|
|
expect.objectContaining({ name: 'editor' }),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const persisted = await Role.findOne({ name: 'editor' }).lean();
|
|
|
|
|
expect(persisted).toBeTruthy();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('trims whitespace from role name', async () => {
|
|
|
|
|
const role = await createRoleByName({ name: ' editor ' });
|
|
|
|
|
|
|
|
|
|
expect(role.name).toBe('editor');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('throws when name is empty', async () => {
|
|
|
|
|
await expect(createRoleByName({ name: '' })).rejects.toThrow('Role name is required');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('throws when name is whitespace-only', async () => {
|
|
|
|
|
await expect(createRoleByName({ name: ' ' })).rejects.toThrow('Role name is required');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('throws when name is undefined', async () => {
|
|
|
|
|
await expect(createRoleByName({})).rejects.toThrow('Role name is required');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('throws for reserved system role names', async () => {
|
|
|
|
|
await expect(createRoleByName({ name: SystemRoles.ADMIN })).rejects.toThrow(
|
|
|
|
|
/reserved system name/,
|
|
|
|
|
);
|
|
|
|
|
await expect(createRoleByName({ name: SystemRoles.USER })).rejects.toThrow(
|
|
|
|
|
/reserved system name/,
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('throws when role already exists', async () => {
|
|
|
|
|
await createRoleByName({ name: 'editor' });
|
|
|
|
|
|
|
|
|
|
await expect(createRoleByName({ name: 'editor' })).rejects.toThrow(/already exists/);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('deleteRoleByName', () => {
|
|
|
|
|
it('deletes a custom role and reassigns users to USER', async () => {
|
|
|
|
|
await createRoleByName({ name: 'editor' });
|
|
|
|
|
await User.create([
|
|
|
|
|
{ name: 'Alice', email: 'alice@test.com', role: 'editor', username: 'alice' },
|
|
|
|
|
{ name: 'Bob', email: 'bob@test.com', role: 'editor', username: 'bob' },
|
|
|
|
|
{ name: 'Carol', email: 'carol@test.com', role: SystemRoles.USER, username: 'carol' },
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
const deleted = await deleteRoleByName('editor');
|
|
|
|
|
|
|
|
|
|
expect(deleted).toBeTruthy();
|
|
|
|
|
expect(deleted!.name).toBe('editor');
|
|
|
|
|
|
|
|
|
|
const alice = await User.findOne({ email: 'alice@test.com' }).lean();
|
|
|
|
|
const bob = await User.findOne({ email: 'bob@test.com' }).lean();
|
|
|
|
|
const carol = await User.findOne({ email: 'carol@test.com' }).lean();
|
|
|
|
|
expect(alice!.role).toBe(SystemRoles.USER);
|
|
|
|
|
expect(bob!.role).toBe(SystemRoles.USER);
|
|
|
|
|
expect(carol!.role).toBe(SystemRoles.USER);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('returns null when role does not exist', async () => {
|
|
|
|
|
const result = await deleteRoleByName('nonexistent');
|
|
|
|
|
expect(result).toBeNull();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('throws for system roles', async () => {
|
|
|
|
|
await expect(deleteRoleByName(SystemRoles.ADMIN)).rejects.toThrow(/Cannot delete system role/);
|
|
|
|
|
await expect(deleteRoleByName(SystemRoles.USER)).rejects.toThrow(/Cannot delete system role/);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('sets cache entry to null after deletion', async () => {
|
|
|
|
|
await createRoleByName({ name: 'editor' });
|
|
|
|
|
mockCache.set.mockClear();
|
|
|
|
|
|
|
|
|
|
await deleteRoleByName('editor');
|
|
|
|
|
|
|
|
|
|
expect(mockCache.set).toHaveBeenCalledWith('editor', null);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('returns null and invalidates cache when role does not exist', async () => {
|
|
|
|
|
mockCache.set.mockClear();
|
|
|
|
|
|
|
|
|
|
const result = await deleteRoleByName('nonexistent');
|
|
|
|
|
|
|
|
|
|
expect(result).toBeNull();
|
|
|
|
|
expect(mockCache.set).toHaveBeenCalledWith('nonexistent', null);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('updateRoleByName - cache on rename', () => {
|
|
|
|
|
it('invalidates old key and populates new key on rename', async () => {
|
|
|
|
|
await createRoleByName({ name: 'editor', description: 'Can edit' });
|
|
|
|
|
mockCache.set.mockClear();
|
|
|
|
|
|
|
|
|
|
const updated = await updateRoleByName('editor', { name: 'senior-editor' });
|
|
|
|
|
|
|
|
|
|
expect(updated.name).toBe('senior-editor');
|
|
|
|
|
expect(mockCache.set).toHaveBeenCalledWith('editor', null);
|
|
|
|
|
expect(mockCache.set).toHaveBeenCalledWith(
|
|
|
|
|
'senior-editor',
|
|
|
|
|
expect.objectContaining({ name: 'senior-editor' }),
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('writes same key when name unchanged', async () => {
|
|
|
|
|
await createRoleByName({ name: 'editor' });
|
|
|
|
|
mockCache.set.mockClear();
|
|
|
|
|
|
|
|
|
|
await updateRoleByName('editor', { description: 'Updated desc' });
|
|
|
|
|
|
|
|
|
|
expect(mockCache.set).toHaveBeenCalledWith(
|
|
|
|
|
'editor',
|
|
|
|
|
expect.objectContaining({ name: 'editor', description: 'Updated desc' }),
|
|
|
|
|
);
|
|
|
|
|
expect(mockCache.set).toHaveBeenCalledTimes(1);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('listUsersByRole', () => {
|
|
|
|
|
it('returns users matching the role', async () => {
|
|
|
|
|
await User.create([
|
|
|
|
|
{ name: 'Alice', email: 'alice@test.com', role: 'editor', username: 'alice' },
|
|
|
|
|
{ name: 'Bob', email: 'bob@test.com', role: 'editor', username: 'bob' },
|
|
|
|
|
{ name: 'Carol', email: 'carol@test.com', role: SystemRoles.USER, username: 'carol' },
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
const users = await listUsersByRole('editor');
|
|
|
|
|
|
|
|
|
|
expect(users).toHaveLength(2);
|
|
|
|
|
const names = users.map((u) => u.name).sort();
|
|
|
|
|
expect(names).toEqual(['Alice', 'Bob']);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('returns empty array when no users have the role', async () => {
|
|
|
|
|
const users = await listUsersByRole('nonexistent');
|
|
|
|
|
expect(users).toEqual([]);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('respects limit and offset for pagination', async () => {
|
|
|
|
|
await User.create([
|
|
|
|
|
{ name: 'Alice', email: 'a@test.com', role: 'editor', username: 'a' },
|
|
|
|
|
{ name: 'Bob', email: 'b@test.com', role: 'editor', username: 'b' },
|
|
|
|
|
{ name: 'Carol', email: 'c@test.com', role: 'editor', username: 'c' },
|
|
|
|
|
{ name: 'Dave', email: 'd@test.com', role: 'editor', username: 'd' },
|
|
|
|
|
{ name: 'Eve', email: 'e@test.com', role: 'editor', username: 'e' },
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
const page1 = await listUsersByRole('editor', { limit: 2, offset: 0 });
|
|
|
|
|
const page2 = await listUsersByRole('editor', { limit: 2, offset: 2 });
|
|
|
|
|
const page3 = await listUsersByRole('editor', { limit: 2, offset: 4 });
|
|
|
|
|
|
|
|
|
|
expect(page1).toHaveLength(2);
|
|
|
|
|
expect(page2).toHaveLength(2);
|
|
|
|
|
expect(page3).toHaveLength(1);
|
|
|
|
|
|
|
|
|
|
const allIds = [...page1, ...page2, ...page3].map((u) => u._id!.toString());
|
|
|
|
|
expect(new Set(allIds).size).toBe(5);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('selects only expected fields', async () => {
|
|
|
|
|
await User.create({
|
|
|
|
|
name: 'Alice',
|
|
|
|
|
email: 'alice@test.com',
|
|
|
|
|
role: 'editor',
|
|
|
|
|
username: 'alice',
|
|
|
|
|
password: 'secret123',
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const users = await listUsersByRole('editor');
|
|
|
|
|
|
|
|
|
|
expect(users).toHaveLength(1);
|
|
|
|
|
expect(users[0].name).toBe('Alice');
|
|
|
|
|
expect(users[0].email).toBe('alice@test.com');
|
|
|
|
|
expect(users[0]._id).toBeDefined();
|
|
|
|
|
expect('password' in users[0]).toBe(false);
|
|
|
|
|
expect('username' in users[0]).toBe(false);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('updateUsersByRole', () => {
|
|
|
|
|
it('migrates all users from one role to another', async () => {
|
|
|
|
|
await User.create([
|
|
|
|
|
{ name: 'Alice', email: 'alice@test.com', role: 'editor', username: 'alice' },
|
|
|
|
|
{ name: 'Bob', email: 'bob@test.com', role: 'editor', username: 'bob' },
|
|
|
|
|
{ name: 'Carol', email: 'carol@test.com', role: SystemRoles.USER, username: 'carol' },
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
await updateUsersByRole('editor', 'senior-editor');
|
|
|
|
|
|
|
|
|
|
const alice = await User.findOne({ email: 'alice@test.com' }).lean();
|
|
|
|
|
const bob = await User.findOne({ email: 'bob@test.com' }).lean();
|
|
|
|
|
const carol = await User.findOne({ email: 'carol@test.com' }).lean();
|
|
|
|
|
expect(alice!.role).toBe('senior-editor');
|
|
|
|
|
expect(bob!.role).toBe('senior-editor');
|
|
|
|
|
expect(carol!.role).toBe(SystemRoles.USER);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('is a no-op when no users have the source role', async () => {
|
|
|
|
|
await User.create({
|
|
|
|
|
name: 'Alice',
|
|
|
|
|
email: 'alice@test.com',
|
|
|
|
|
role: SystemRoles.USER,
|
|
|
|
|
username: 'alice',
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await updateUsersByRole('nonexistent', 'new-role');
|
|
|
|
|
|
|
|
|
|
const alice = await User.findOne({ email: 'alice@test.com' }).lean();
|
|
|
|
|
expect(alice!.role).toBe(SystemRoles.USER);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('countUsersByRole', () => {
|
|
|
|
|
it('returns the count of users with the given role', async () => {
|
|
|
|
|
await User.create([
|
|
|
|
|
{ name: 'Alice', email: 'alice@test.com', role: 'editor', username: 'alice' },
|
|
|
|
|
{ name: 'Bob', email: 'bob@test.com', role: 'editor', username: 'bob' },
|
|
|
|
|
{ name: 'Carol', email: 'carol@test.com', role: SystemRoles.USER, username: 'carol' },
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
expect(await countUsersByRole('editor')).toBe(2);
|
|
|
|
|
expect(await countUsersByRole(SystemRoles.USER)).toBe(1);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('returns 0 when no users have the role', async () => {
|
|
|
|
|
expect(await countUsersByRole('nonexistent')).toBe(0);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('listRoles', () => {
|
|
|
|
|
beforeEach(async () => {
|
|
|
|
|
await Role.deleteMany({});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('returns roles sorted alphabetically by name', async () => {
|
|
|
|
|
await Role.create([
|
|
|
|
|
{ name: 'zebra', permissions: {} },
|
|
|
|
|
{ name: 'alpha', permissions: {} },
|
|
|
|
|
{ name: 'middle', permissions: {} },
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
const roles = await listRoles();
|
|
|
|
|
|
|
|
|
|
expect(roles.map((r) => r.name)).toEqual(['alpha', 'middle', 'zebra']);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('respects limit and offset for pagination', async () => {
|
|
|
|
|
await Role.create([
|
|
|
|
|
{ name: 'a-role', permissions: {} },
|
|
|
|
|
{ name: 'b-role', permissions: {} },
|
|
|
|
|
{ name: 'c-role', permissions: {} },
|
|
|
|
|
{ name: 'd-role', permissions: {} },
|
|
|
|
|
{ name: 'e-role', permissions: {} },
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
const page1 = await listRoles({ limit: 2, offset: 0 });
|
|
|
|
|
const page2 = await listRoles({ limit: 2, offset: 2 });
|
|
|
|
|
const page3 = await listRoles({ limit: 2, offset: 4 });
|
|
|
|
|
|
|
|
|
|
expect(page1).toHaveLength(2);
|
|
|
|
|
expect(page1.map((r) => r.name)).toEqual(['a-role', 'b-role']);
|
|
|
|
|
expect(page2).toHaveLength(2);
|
|
|
|
|
expect(page2.map((r) => r.name)).toEqual(['c-role', 'd-role']);
|
|
|
|
|
expect(page3).toHaveLength(1);
|
|
|
|
|
expect(page3.map((r) => r.name)).toEqual(['e-role']);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('defaults to limit 50 and offset 0', async () => {
|
|
|
|
|
await Role.create({ name: 'only-role', permissions: {} });
|
|
|
|
|
|
|
|
|
|
const roles = await listRoles();
|
|
|
|
|
|
|
|
|
|
expect(roles).toHaveLength(1);
|
|
|
|
|
expect(roles[0].name).toBe('only-role');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('returns only name and description fields', async () => {
|
|
|
|
|
await Role.create({
|
|
|
|
|
name: 'editor',
|
|
|
|
|
description: 'Can edit',
|
|
|
|
|
permissions: { PROMPTS: { USE: true } },
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const roles = await listRoles();
|
|
|
|
|
|
|
|
|
|
expect(roles).toHaveLength(1);
|
|
|
|
|
expect(roles[0].name).toBe('editor');
|
|
|
|
|
expect(roles[0].description).toBe('Can edit');
|
|
|
|
|
expect(roles[0]._id).toBeDefined();
|
|
|
|
|
expect('permissions' in roles[0]).toBe(false);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('returns empty array when no roles exist', async () => {
|
|
|
|
|
const roles = await listRoles();
|
|
|
|
|
expect(roles).toEqual([]);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('returns undefined description for pre-existing roles without the field', async () => {
|
|
|
|
|
await Role.collection.insertOne({ name: 'legacy', permissions: {} });
|
|
|
|
|
|
|
|
|
|
const roles = await listRoles();
|
|
|
|
|
|
|
|
|
|
expect(roles).toHaveLength(1);
|
|
|
|
|
expect(roles[0].name).toBe('legacy');
|
|
|
|
|
expect(roles[0].description).toBeUndefined();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('countRoles', () => {
|
|
|
|
|
beforeEach(async () => {
|
|
|
|
|
await Role.deleteMany({});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('returns the total number of roles', async () => {
|
|
|
|
|
await Role.create([
|
|
|
|
|
{ name: 'a', permissions: {} },
|
|
|
|
|
{ name: 'b', permissions: {} },
|
|
|
|
|
{ name: 'c', permissions: {} },
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
expect(await countRoles()).toBe(3);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('returns 0 when no roles exist', async () => {
|
|
|
|
|
expect(await countRoles()).toBe(0);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('createRoleByName - duplicate key race', () => {
|
|
|
|
|
beforeEach(async () => {
|
|
|
|
|
await Role.deleteMany({});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('throws RoleConflictError on concurrent insert (11000)', async () => {
|
|
|
|
|
await createRoleByName({ name: 'editor' });
|
|
|
|
|
|
|
|
|
|
const insertSpy = jest.spyOn(Role.prototype, 'save').mockImplementationOnce(() => {
|
|
|
|
|
const err = new Error('E11000 duplicate key error') as Error & { code: number };
|
|
|
|
|
err.code = 11000;
|
|
|
|
|
throw err;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await expect(createRoleByName({ name: 'editor2' })).rejects.toThrow(/already exists/);
|
|
|
|
|
|
|
|
|
|
insertSpy.mockRestore();
|
|
|
|
|
});
|
|
|
|
|
});
|