diff --git a/api/test-role.js b/api/test-role.js index fc4ee0187e..aee14451b6 100644 --- a/api/test-role.js +++ b/api/test-role.js @@ -1,10 +1,13 @@ require('dotenv').config({ path: '../.env' }); +const mongoose = require('mongoose'); const connect = require('../config/connect'); -const { Role } = require('@librechat/data-schemas'); +const { createRoleMethods, createRoleModel } = require('@librechat/data-schemas'); +createRoleModel(mongoose); +const { listRoles } = createRoleMethods(mongoose); (async () => { await connect(); console.log('Connected to database'); - const role = await Role.findOne({ name: 'ADMIN' }); - console.log(role); -})(); \ No newline at end of file + const roles = await listRoles(); + console.dir(roles, { depth: null }); +})(); diff --git a/packages/data-schemas/src/index.ts b/packages/data-schemas/src/index.ts index 275707651f..6f1521e755 100644 --- a/packages/data-schemas/src/index.ts +++ b/packages/data-schemas/src/index.ts @@ -1,4 +1,6 @@ -export { Role } from './models/role'; +// export { default as roleSchema } from './schema/role'; +export { createRoleModel } from './models/role'; +export { createRoleMethods } from './methods/role'; // export { default as logger } from './config/winston'; // export { default as meiliLogger } from './config/meiliLogger'; // export * from './types'; diff --git a/packages/data-schemas/src/methods/README.md b/packages/data-schemas/src/methods/README.md deleted file mode 100644 index af7cc03a29..0000000000 --- a/packages/data-schemas/src/methods/README.md +++ /dev/null @@ -1,97 +0,0 @@ -# Methods - -This directory contains pure functions that replace the static methods from the schema files. This refactoring improves testability, type safety, and code modularity. - -## Structure - -- `userMethods.ts` - Functions for user operations -- `sessionMethods.ts` - Functions for session operations -- `tokenMethods.ts` - Functions for token operations -- `index.ts` - Exports all methods for convenient importing - -## Migration from Static Methods - -Instead of calling static methods on models: - -```typescript -// OLD: Using static methods -const user = await UserModel.findUser({ email: 'test@example.com' }); -const result = await UserModel.deleteUserById(userId); -``` - -Use the pure functions with the model as the first parameter: - -```typescript -// NEW: Using pure functions -import { findUser, deleteUserById } from '~/methods'; -import UserModel from '~/schema/user'; - -const user = await findUser(UserModel, { email: 'test@example.com' }); -const result = await deleteUserById(UserModel, userId); -``` - -## Benefits - -1. **Pure Functions**: Methods are now side-effect free and testable -2. **Better Types**: Proper TypeScript typing throughout -3. **Dependency Injection**: Models are passed as parameters -4. **Modular**: Functions can be imported individually or as a group -5. **No Magic**: Clear explicit dependencies - -## Usage Examples - -### User Methods - -```typescript -import { createUser, findUser, updateUser } from '~/methods'; -import UserModel from '~/schema/user'; - -// Create a user -const newUser = await createUser( - UserModel, - { email: 'user@example.com', name: 'John' }, - { enabled: true, startBalance: 100 } -); - -// Find a user -const user = await findUser(UserModel, { email: 'user@example.com' }); - -// Update a user -const updated = await updateUser(UserModel, userId, { name: 'Jane' }); -``` - -### Session Methods - -```typescript -import { createSession, findSession, deleteSession } from '~/methods'; -import SessionModel from '~/schema/session'; - -// Create session -const { session, refreshToken } = await createSession(SessionModel, userId); - -// Find session -const foundSession = await findSession(SessionModel, { refreshToken }); - -// Delete session -await deleteSession(SessionModel, { sessionId }); -``` - -### Token Methods - -```typescript -import { createToken, findToken, deleteTokens } from '~/methods'; -import TokenModel from '~/schema/token'; - -// Create token -const token = await createToken(TokenModel, { - userId, - token: 'abc123', - expiresIn: 3600 -}); - -// Find token -const foundToken = await findToken(TokenModel, { token: 'abc123' }); - -// Delete tokens -await deleteTokens(TokenModel, { userId }); -``` \ No newline at end of file diff --git a/packages/data-schemas/src/methods/role.ts b/packages/data-schemas/src/methods/role.ts new file mode 100644 index 0000000000..208636f7f0 --- /dev/null +++ b/packages/data-schemas/src/methods/role.ts @@ -0,0 +1,50 @@ +import { roleDefaults, SystemRoles } from 'librechat-data-provider'; + +// Factory function that takes mongoose instance and returns the methods +export function createRoleMethods(mongoose: typeof import('mongoose')) { + /** + * Initialize default roles in the system. + * Creates the default roles (ADMIN, USER) if they don't exist in the database. + * Updates existing roles with new permission types if they're missing. + */ + async function initializeRoles() { + const Role = mongoose.models.Role; + + for (const roleName of [SystemRoles.ADMIN, SystemRoles.USER]) { + let role = await Role.findOne({ name: roleName }); + const defaultPerms = roleDefaults[roleName].permissions; + + if (!role) { + // Create new role if it doesn't exist. + role = new Role(roleDefaults[roleName]); + } else { + // Ensure role.permissions is defined. + role.permissions = role.permissions || {}; + // For each permission type in defaults, add it if missing. + for (const permType of Object.keys(defaultPerms)) { + if (role.permissions[permType] == null) { + role.permissions[permType] = defaultPerms[permType as keyof typeof defaultPerms]; + } + } + } + await role.save(); + } + } + + /** + * List all roles in the system (for testing purposes) + * Returns an array of all roles with their names and permissions + */ + async function listRoles() { + const Role = mongoose.models.Role; + return await Role.find({}).select('name permissions').lean(); + } + + // Return all methods you want to expose + return { + listRoles, + initializeRoles, + }; +} + +export type RoleMethods = ReturnType; diff --git a/packages/data-schemas/src/models/role.ts b/packages/data-schemas/src/models/role.ts index ea9432633a..ccc56af1d6 100644 --- a/packages/data-schemas/src/models/role.ts +++ b/packages/data-schemas/src/models/role.ts @@ -1,5 +1,9 @@ -import mongoose from '~/mongoose'; import roleSchema from '~/schema/role'; import type { IRole } from '~/types'; -export const Role = mongoose.models.Role || mongoose.model('Role', roleSchema) as any; +/** + * Creates or returns the Role model using the provided mongoose instance and schema + */ +export function createRoleModel(mongoose: typeof import('mongoose')) { + return mongoose.models.Role || mongoose.model('Role', roleSchema); +} diff --git a/packages/data-schemas/src/schema/role.ts b/packages/data-schemas/src/schema/role.ts index 012ce9cb39..bd20fa4ca0 100644 --- a/packages/data-schemas/src/schema/role.ts +++ b/packages/data-schemas/src/schema/role.ts @@ -1,4 +1,4 @@ -import { Schema } from '~/mongoose'; +import { Schema } from 'mongoose'; import { PermissionTypes, Permissions } from 'librechat-data-provider'; import type { IRole } from '~/types';