diff --git a/api/app/clients/tools/util/handleTools.test.js b/api/app/clients/tools/util/handleTools.test.js index 5ad65227f0..7f2411b328 100644 --- a/api/app/clients/tools/util/handleTools.test.js +++ b/api/app/clients/tools/util/handleTools.test.js @@ -21,25 +21,6 @@ jest.mock('~/lib/db/connectDb', () => { }, }; }); -jest.mock('@librechat/data-schemas', () => { - const userModelMock = { - createUser: jest.fn(() => mockUser), - findUser: jest.fn(), - updateUser: jest.fn(), - }; - return { - registerModels: jest.fn().mockReturnValue({ - User: userModelMock, - }), - }; -}); - -jest.mock('~/models/Message', () => ({ - Message: jest.fn(), -})); -jest.mock('~/models/Conversation', () => ({ - Conversation: jest.fn(), -})); jest.mock('~/models/File', () => ({ File: jest.fn(), })); @@ -63,9 +44,6 @@ describe('Tool Handlers', () => { const mainPlugin = availableTools.find((tool) => tool.pluginKey === pluginKey); const authConfigs = mainPlugin.authConfig; - const { registerModels } = require('@librechat/data-schemas'); - let User = registerModels().User; - beforeAll(async () => { mockUser.save.mockResolvedValue(undefined); diff --git a/api/cache/banViolation.js b/api/cache/banViolation.js index 6dfdee5c62..7072dae968 100644 --- a/api/cache/banViolation.js +++ b/api/cache/banViolation.js @@ -1,8 +1,8 @@ const { ViolationTypes } = require('librechat-data-provider'); +const { deleteAllUserSessions } = require('@librechat/data-schemas'); const { isEnabled, math, removePorts } = require('~/server/utils'); const getLogStores = require('./getLogStores'); const { logger } = require('~/config'); -const db = require('~/lib/db/connectDb'); const { BAN_VIOLATIONS, BAN_INTERVAL } = process.env ?? {}; const interval = math(BAN_INTERVAL, 20); @@ -45,7 +45,7 @@ const banViolation = async (req, res, errorMessage) => { return; } - await db.models.Session.deleteAllUserSessions({ userId: user_id }); + await deleteAllUserSessions({ userId: user_id }); res.clearCookie('refreshToken'); const banLogs = getLogStores(ViolationTypes.BAN); diff --git a/api/cache/banViolation.spec.js b/api/cache/banViolation.spec.js index ebe5ce8aa9..162f97143e 100644 --- a/api/cache/banViolation.spec.js +++ b/api/cache/banViolation.spec.js @@ -1,17 +1,5 @@ const banViolation = require('./banViolation'); -jest.mock('@librechat/data-schemas', () => { - const sessionModelMock = { - deleteAllUserSessions: jest.fn(), - }; - - return { - registerModels: jest.fn().mockReturnValue({ - Session: sessionModelMock, - }), - }; -}); - const mockModels = { Session: { deleteAllUserSessions: jest.fn(), diff --git a/api/lib/db/connectDb.js b/api/lib/db/connectDb.js index 98b83c19c8..54207e0f33 100644 --- a/api/lib/db/connectDb.js +++ b/api/lib/db/connectDb.js @@ -1,6 +1,5 @@ require('dotenv').config(); const mongoose = require('mongoose'); -const { registerModels } = require('@librechat/data-schemas'); if (!process.env.MONGO_URI) { throw new Error('Please define the MONGO_URI environment variable'); @@ -40,11 +39,6 @@ async function connectDb(mongoUri = process.env.MONGO_URI) { } cached.conn = await cached.promise; - // Register models once - if (!cached.models) { - cached.models = registerModels(mongoose); - } - return cached.conn; } diff --git a/api/lib/db/indexSync.js b/api/lib/db/indexSync.js index de1f880287..7a22d907da 100644 --- a/api/lib/db/indexSync.js +++ b/api/lib/db/indexSync.js @@ -1,7 +1,6 @@ const { MeiliSearch } = require('meilisearch'); +const { Message, Conversation, logger } = require('@librechat/data-schemas'); const { isEnabled } = require('~/server/utils'); -const { logger } = require('~/config'); -const db = require('~/lib/db/connectDb'); const searchEnabled = isEnabled(process.env.SEARCH); const indexingDisabled = isEnabled(process.env.MEILI_NO_SYNC); @@ -28,7 +27,6 @@ async function indexSync() { if (!searchEnabled) { return; } - const { Message, Conversation } = db.models; try { const client = MeiliSearchClient.getInstance(); @@ -42,6 +40,8 @@ async function indexSync() { return; } + logger.info('[indexSync] Starting index sync...'); + const messageCount = await Message.countDocuments(); const convoCount = await Conversation.countDocuments(); const messages = await client.index('messages').getStats(); diff --git a/api/models/Agent.js b/api/models/Agent.js index 62c0f1e125..013c1b4210 100644 --- a/api/models/Agent.js +++ b/api/models/Agent.js @@ -1,5 +1,6 @@ const mongoose = require('mongoose'); const crypto = require('node:crypto'); +const { Agent } = require('@librechat/data-schemas'); const { SystemRoles, Tools, actionDelimiter } = require('librechat-data-provider'); const { GLOBAL_PROJECT_NAME, EPHEMERAL_AGENT_ID, mcp_delimiter } = require('librechat-data-provider').Constants; @@ -14,8 +15,6 @@ const getLogStores = require('~/cache/getLogStores'); const { getActions } = require('./Action'); const { logger } = require('~/config'); -const db = require('~/lib/db/connectDb'); - /** * Create an agent with the provided data. * @param {Object} agentData - The agent data to create. @@ -35,7 +34,7 @@ const createAgent = async (agentData) => { }, ], }; - return (await db.models.Agent.create(initialAgentData)).toObject(); + return (await Agent.create(initialAgentData)).toObject(); }; /** @@ -46,7 +45,7 @@ const createAgent = async (agentData) => { * @param {string} searchParameter.author - The user ID of the agent's author. * @returns {Promise} The agent document as a plain object, or null if not found. */ -const getAgent = async (searchParameter) => await db.models.Agent.findOne(searchParameter).lean(); +const getAgent = async (searchParameter) => await Agent.findOne(searchParameter).lean(); /** * Load an agent based on the provided ID @@ -268,7 +267,6 @@ const updateAgent = async (searchParameter, updateData, options = {}) => { const { updatingUserId = null, forceVersion = false } = options; const mongoOptions = { new: true, upsert: false }; - const Agent = db.models?.Agent; const currentAgent = await Agent.findOne(searchParameter); if (currentAgent) { const { __v, _id, id, versions, author, ...versionData } = currentAgent.toObject(); @@ -362,7 +360,6 @@ const updateAgent = async (searchParameter, updateData, options = {}) => { * @returns {Promise} The updated agent. */ const addAgentResourceFile = async ({ req, agent_id, tool_resource, file_id }) => { - const Agent = db.models?.Agent; const searchParameter = { id: agent_id }; let agent = await getAgent(searchParameter); if (!agent) { @@ -428,7 +425,7 @@ const removeAgentResourceFiles = async ({ agent_id, files }) => { } const updatePullData = { $pull: pullOps }; - const agentAfterPull = await db.models.Agent.findOneAndUpdate(searchParameter, updatePullData, { + const agentAfterPull = await Agent.findOneAndUpdate(searchParameter, updatePullData, { new: true, }).lean(); @@ -458,7 +455,7 @@ const removeAgentResourceFiles = async ({ agent_id, files }) => { * @returns {Promise} Resolves when the agent has been successfully deleted. */ const deleteAgent = async (searchParameter) => { - const agent = await db.models.Agent.findOneAndDelete(searchParameter); + const agent = await Agent.findOneAndDelete(searchParameter); if (agent) { await removeAgentFromAllProjects(agent.id); } @@ -483,7 +480,7 @@ const getListAgents = async (searchParameter) => { query = { $or: [globalQuery, query] }; } const agents = ( - await db.models.Agent.find(query, { + await Agent.find(query, { id: 1, _id: 0, name: 1, @@ -580,7 +577,6 @@ const updateAgentProjects = async ({ user, agentId, projectIds, removeProjectIds * @throws {Error} If the agent is not found or the specified version does not exist. */ const revertAgentVersion = async (searchParameter, versionIndex) => { - const Agent = db.models?.Agent; const agent = await Agent.findOne(searchParameter); if (!agent) { throw new Error('Agent not found'); diff --git a/api/models/Agent.spec.js b/api/models/Agent.spec.js index 4bfe4243dd..b935d506d3 100644 --- a/api/models/Agent.spec.js +++ b/api/models/Agent.spec.js @@ -8,6 +8,7 @@ process.env.CREDS_IV = '0123456789abcdef'; const mongoose = require('mongoose'); const { v4: uuidv4 } = require('uuid'); +const { Agent } = require('@librechat/data-schemas'); const { MongoMemoryServer } = require('mongodb-memory-server'); const { addAgentResourceFile, @@ -19,9 +20,6 @@ const { getListAgents, updateAgentProjects, } = require('./Agent'); -const db = require('~/lib/db/connectDb'); - -let Agent; describe('Agent Resource File Operations', () => { let mongoServer; @@ -29,9 +27,7 @@ describe('Agent Resource File Operations', () => { beforeAll(async () => { mongoServer = await MongoMemoryServer.create(); const mongoUri = mongoServer.getUri(); - await db.connectDb(mongoUri); - - Agent = db.models.Agent; + await mongoose.connect(mongoUri); }); afterAll(async () => { diff --git a/api/models/Assistant.js b/api/models/Assistant.js index 079867cf73..99282171bf 100644 --- a/api/models/Assistant.js +++ b/api/models/Assistant.js @@ -1,4 +1,4 @@ -const db = require('~/lib/db/connectDb'); +const { Assistant } = require('@librechat/data-schemas'); /** * Update an assistant with new data without overwriting existing properties, @@ -12,7 +12,7 @@ const db = require('~/lib/db/connectDb'); */ const updateAssistantDoc = async (searchParams, updateData) => { const options = { new: true, upsert: true }; - return await db.models.Assistant.findOneAndUpdate(searchParams, updateData, options).lean(); + return await Assistant.findOneAndUpdate(searchParams, updateData, options).lean(); }; /** @@ -23,7 +23,7 @@ const updateAssistantDoc = async (searchParams, updateData) => { * @param {string} searchParams.user - The user ID of the assistant's author. * @returns {Promise} The assistant document as a plain object, or null if not found. */ -const getAssistant = async (searchParams) => await db.models.Assistant.findOne(searchParams).lean(); +const getAssistant = async (searchParams) => await Assistant.findOne(searchParams).lean(); /** * Retrieves all assistants that match the given search parameters. @@ -33,7 +33,7 @@ const getAssistant = async (searchParams) => await db.models.Assistant.findOne(s * @returns {Promise>} A promise that resolves to an array of assistant documents as plain objects. */ const getAssistants = async (searchParams, select = null) => { - let query = db.models.Assistant.find(searchParams); + let query = Assistant.find(searchParams); if (select) { query = query.select(select); @@ -51,7 +51,7 @@ const getAssistants = async (searchParams, select = null) => { * @returns {Promise} Resolves when the assistant has been successfully deleted. */ const deleteAssistant = async (searchParams) => { - return await db.models.Assistant.findOneAndDelete(searchParams); + return await Assistant.findOneAndDelete(searchParams); }; module.exports = { diff --git a/api/models/Banner.js b/api/models/Banner.js index 79092c3bdb..4345abc4fe 100644 --- a/api/models/Banner.js +++ b/api/models/Banner.js @@ -1,5 +1,4 @@ -const logger = require('~/config/winston'); -const db = require('~/lib/db/connectDb'); +const { Banner, logger } = require('@librechat/data-schemas'); /** * Retrieves the current active banner. @@ -8,7 +7,7 @@ const db = require('~/lib/db/connectDb'); const getBanner = async (user) => { try { const now = new Date(); - const banner = await db.models.Banner.findOne({ + const banner = await Banner.findOne({ displayFrom: { $lte: now }, $or: [{ displayTo: { $gte: now } }, { displayTo: null }], type: 'banner', diff --git a/api/models/Conversation.js b/api/models/Conversation.js index 270da2c81f..219c7c6c92 100644 --- a/api/models/Conversation.js +++ b/api/models/Conversation.js @@ -1,6 +1,5 @@ +const { Conversation, logger } = require('@librechat/data-schemas'); const { getMessages, deleteMessages } = require('./Message'); -const logger = require('~/config/winston'); -const db = require('~/lib/db/connectDb'); /** * Searches for a conversation by conversationId and returns a lean document with only conversationId and user. * @param {string} conversationId - The conversation's ID. @@ -8,7 +7,7 @@ const db = require('~/lib/db/connectDb'); */ const searchConversation = async (conversationId) => { try { - return await db.models.Conversation.findOne({ conversationId }, 'conversationId user').lean(); + return await Conversation.findOne({ conversationId }, 'conversationId user').lean(); } catch (error) { logger.error('[searchConversation] Error searching conversation', error); throw new Error('Error searching conversation'); @@ -23,7 +22,7 @@ const searchConversation = async (conversationId) => { */ const getConvo = async (user, conversationId) => { try { - return await db.models.Conversation.findOne({ user, conversationId }).lean(); + return await Conversation.findOne({ user, conversationId }).lean(); } catch (error) { logger.error('[getConvo] Error getting single conversation', error); return { message: 'Error getting single conversation' }; @@ -40,7 +39,7 @@ const deleteNullOrEmptyConversations = async () => { ], }; - const result = await db.models.Conversation.deleteMany(filter); + const result = await Conversation.deleteMany(filter); // Delete associated messages const messageDeleteResult = await deleteMessages(filter); @@ -66,7 +65,7 @@ const deleteNullOrEmptyConversations = async () => { */ const getConvoFiles = async (conversationId) => { try { - return (await db.models.Conversation.findOne({ conversationId }, 'files').lean())?.files ?? []; + return (await Conversation.findOne({ conversationId }, 'files').lean())?.files ?? []; } catch (error) { logger.error('[getConvoFiles] Error getting conversation files', error); throw new Error('Error getting conversation files'); @@ -112,7 +111,7 @@ module.exports = { } /** Note: the resulting Model object is necessary for Meilisearch operations */ - const conversation = await db.models.Conversation.findOneAndUpdate( + const conversation = await Conversation.findOneAndUpdate( { conversationId, user: req.user.id }, updateOperation, { @@ -141,7 +140,7 @@ module.exports = { }, })); - const result = await db.models.Conversation.bulkWrite(bulkOps); + const result = await Conversation.bulkWrite(bulkOps); return result; } catch (error) { logger.error('[saveBulkConversations] Error saving conversations in bulk', error); @@ -153,7 +152,6 @@ module.exports = { { cursor, limit = 25, isArchived = false, tags, search, order = 'desc' } = {}, ) => { const filters = [{ user }]; - const { Conversation } = db.models; if (isArchived) { filters.push({ isArchived: true }); } else { @@ -217,7 +215,7 @@ module.exports = { const conversationIds = convoIds.map((convo) => convo.conversationId); - const results = await db.models.Conversation.find({ + const results = await Conversation.find({ user, conversationId: { $in: conversationIds }, $or: [{ expiredAt: { $exists: false } }, { expiredAt: null }], @@ -286,7 +284,6 @@ module.exports = { deleteConvos: async (user, filter) => { try { const userFilter = { ...filter, user }; - const { Conversation } = db.models; const conversations = await Conversation.find(userFilter).select('conversationId'); const conversationIds = conversations.map((c) => c.conversationId); diff --git a/api/models/ConversationTag.js b/api/models/ConversationTag.js index 81716ff14e..ffee78b1af 100644 --- a/api/models/ConversationTag.js +++ b/api/models/ConversationTag.js @@ -1,5 +1,4 @@ -const logger = require('~/config/winston'); -const db = require('~/lib/db/connectDb'); +const { ConversationTag, Conversation, logger } = require('@librechat/data-schemas'); /** * Retrieves all conversation tags for a user. @@ -8,7 +7,7 @@ const db = require('~/lib/db/connectDb'); */ const getConversationTags = async (user) => { try { - return await db.models.ConversationTag.find({ user }).sort({ position: 1 }).lean(); + return await ConversationTag.find({ user }).sort({ position: 1 }).lean(); } catch (error) { logger.error('[getConversationTags] Error getting conversation tags', error); throw new Error('Error getting conversation tags'); @@ -29,7 +28,6 @@ const createConversationTag = async (user, data) => { try { const { tag, description, addToConversation, conversationId } = data; - const { ConversationTag, Conversation } = db.models; const existingTag = await ConversationTag.findOne({ user, tag }).lean(); if (existingTag) { return existingTag; @@ -84,7 +82,6 @@ const updateConversationTag = async (user, oldTag, data) => { try { const { tag: newTag, description, position } = data; - const { ConversationTag, Conversation } = db.models; const existingTag = await ConversationTag.findOne({ user, tag: oldTag }).lean(); if (!existingTag) { return null; @@ -145,7 +142,7 @@ const adjustPositions = async (user, oldPosition, newPosition) => { $lt: Math.max(oldPosition, newPosition), }; - await db.models.ConversationTag.updateMany( + await ConversationTag.updateMany( { user, position, @@ -162,7 +159,6 @@ const adjustPositions = async (user, oldPosition, newPosition) => { */ const deleteConversationTag = async (user, tag) => { try { - const { ConversationTag, Conversation } = db.models; const deletedTag = await ConversationTag.findOneAndDelete({ user, tag }).lean(); if (!deletedTag) { return null; @@ -191,7 +187,6 @@ const deleteConversationTag = async (user, tag) => { */ const updateTagsForConversation = async (user, conversationId, tags) => { try { - const { ConversationTag, Conversation } = db.models; const conversation = await Conversation.findOne({ user, conversationId }).lean(); if (!conversation) { throw new Error('Conversation not found'); diff --git a/api/models/File.js b/api/models/File.js index 7755a0d9c8..52177d287e 100644 --- a/api/models/File.js +++ b/api/models/File.js @@ -1,7 +1,7 @@ const mongoose = require('mongoose'); +const { File } = require('@librechat/data-schemas'); const { EToolResources } = require('librechat-data-provider'); const { logger } = require('~/config'); -const db = require('~/lib/db/connectDb'); /** * Finds a file by its file_id with additional query options. @@ -10,7 +10,7 @@ const db = require('~/lib/db/connectDb'); * @returns {Promise} A promise that resolves to the file document or null. */ const findFileById = async (file_id, options = {}) => { - return await db.models.File.findOne({ file_id, ...options }).lean(); + return await File.findOne({ file_id, ...options }).lean(); }; /** @@ -23,7 +23,7 @@ const findFileById = async (file_id, options = {}) => { */ const getFiles = async (filter, _sortOptions, selectFields = { text: 0 }) => { const sortOptions = { updatedAt: -1, ..._sortOptions }; - return await db.models.File.find(filter).select(selectFields).sort(sortOptions).lean(); + return await File.find(filter).select(selectFields).sort(sortOptions).lean(); }; /** @@ -79,7 +79,7 @@ const createFile = async (data, disableTTL) => { delete fileData.expiresAt; } - return await db.models.File.findOneAndUpdate({ file_id: data.file_id }, fileData, { + return await File.findOneAndUpdate({ file_id: data.file_id }, fileData, { new: true, upsert: true, }).lean(); @@ -96,7 +96,7 @@ const updateFile = async (data) => { $set: update, $unset: { expiresAt: '' }, // Remove the expiresAt field to prevent TTL }; - return await db.models.File.findOneAndUpdate({ file_id }, updateOperation, { new: true }).lean(); + return await File.findOneAndUpdate({ file_id }, updateOperation, { new: true }).lean(); }; /** @@ -110,7 +110,7 @@ const updateFileUsage = async (data) => { $inc: { usage: inc }, $unset: { expiresAt: '', temp_file_id: '' }, }; - return await db.models.File.findOneAndUpdate({ file_id }, updateOperation, { new: true }).lean(); + return await File.findOneAndUpdate({ file_id }, updateOperation, { new: true }).lean(); }; /** @@ -119,7 +119,7 @@ const updateFileUsage = async (data) => { * @returns {Promise} A promise that resolves to the deleted file document or null. */ const deleteFile = async (file_id) => { - return await db.models.File.findOneAndDelete({ file_id }).lean(); + return await File.findOneAndDelete({ file_id }).lean(); }; /** @@ -128,7 +128,7 @@ const deleteFile = async (file_id) => { * @returns {Promise} A promise that resolves to the deleted file document or null. */ const deleteFileByFilter = async (filter) => { - return await db.models.File.findOneAndDelete(filter).lean(); + return await File.findOneAndDelete(filter).lean(); }; /** @@ -141,7 +141,7 @@ const deleteFiles = async (file_ids, user) => { if (user) { deleteQuery = { user: user }; } - return await db.models.File.deleteMany(deleteQuery); + return await File.deleteMany(deleteQuery); }; /** diff --git a/api/models/Message.js b/api/models/Message.js index 78b7976bec..c4d2102618 100644 --- a/api/models/Message.js +++ b/api/models/Message.js @@ -1,6 +1,5 @@ const { z } = require('zod'); -const { logger } = require('~/config'); -const db = require('~/lib/db/connectDb'); +const { Message, logger } = require('@librechat/data-schemas'); const idSchema = z.string().uuid(); /** @@ -67,7 +66,7 @@ async function saveMessage(req, params, metadata) { logger.info(`---\`saveMessage\` context: ${metadata?.context}`); update.tokenCount = 0; } - const message = await db.models.Message.findOneAndUpdate( + const message = await Message.findOneAndUpdate( { messageId: params.messageId, user: req.user.id }, update, { upsert: true, new: true }, @@ -85,7 +84,7 @@ async function saveMessage(req, params, metadata) { try { // Try to find the existing message with this ID - const existingMessage = await db.models.Message.findOne({ + const existingMessage = await Message.findOne({ messageId: params.messageId, user: req.user.id, }); @@ -138,7 +137,7 @@ async function bulkSaveMessages(messages, overrideTimestamp = false) { upsert: true, }, })); - const result = await db.models.Message.bulkWrite(bulkOps); + const result = await Message.bulkWrite(bulkOps); return result; } catch (err) { logger.error('Error saving messages in bulk:', err); @@ -180,7 +179,7 @@ async function recordMessage({ ...rest, }; - return await db.models.Message.findOneAndUpdate({ user, messageId }, message, { + return await Message.findOneAndUpdate({ user, messageId }, message, { upsert: true, new: true, }); @@ -204,7 +203,7 @@ async function recordMessage({ */ async function updateMessageText(req, { messageId, text }) { try { - await db.models?.Message.updateOne({ messageId, user: req.user.id }, { text }); + await Message.updateOne({ messageId, user: req.user.id }, { text }); } catch (err) { logger.error('Error updating message text:', err); throw err; @@ -232,7 +231,7 @@ async function updateMessageText(req, { messageId, text }) { async function updateMessage(req, message, metadata) { try { const { messageId, ...update } = message; - const updatedMessage = await db.models.Message.findOneAndUpdate( + const updatedMessage = await Message.findOneAndUpdate( { messageId, user: req.user.id }, update, { @@ -276,10 +275,10 @@ async function updateMessage(req, message, metadata) { */ async function deleteMessagesSince(req, { messageId, conversationId }) { try { - const message = await db.models.Message.findOne({ messageId, user: req.user.id }).lean(); + const message = await Message.findOne({ messageId, user: req.user.id }).lean(); if (message) { - const query = db.models.Message.find({ conversationId, user: req.user.id }); + const query = Message.find({ conversationId, user: req.user.id }); return await query.deleteMany({ createdAt: { $gt: message.createdAt }, }); @@ -303,10 +302,10 @@ async function deleteMessagesSince(req, { messageId, conversationId }) { async function getMessages(filter, select) { try { if (select) { - return await db.models.Message.find(filter).select(select).sort({ createdAt: 1 }).lean(); + return await Message.find(filter).select(select).sort({ createdAt: 1 }).lean(); } - return await db.models.Message.find(filter).sort({ createdAt: 1 }).lean(); + return await Message.find(filter).sort({ createdAt: 1 }).lean(); } catch (err) { logger.error('Error getting messages:', err); throw err; @@ -323,7 +322,7 @@ async function getMessages(filter, select) { */ async function getMessage({ user, messageId }) { try { - return await db.models.Message.findOne({ + return await Message.findOne({ user, messageId, }).lean(); @@ -344,7 +343,7 @@ async function getMessage({ user, messageId }) { */ async function deleteMessages(filter) { try { - return await db.models.Message.deleteMany(filter); + return await Message.deleteMany(filter); } catch (err) { logger.error('Error deleting messages:', err); throw err; diff --git a/api/models/Preset.js b/api/models/Preset.js index 0d550fcc11..731675bfb5 100644 --- a/api/models/Preset.js +++ b/api/models/Preset.js @@ -1,9 +1,8 @@ -const { logger } = require('~/config'); -const db = require('~/lib/db/connectDb'); +const { Preset, logger } = require('@librechat/data-schemas'); const getPreset = async (user, presetId) => { try { - return await db.models.Preset.findOne({ user, presetId }).lean(); + return await Preset.findOne({ user, presetId }).lean(); } catch (error) { logger.error('[getPreset] Error getting single preset', error); return { message: 'Error getting single preset' }; @@ -14,7 +13,7 @@ module.exports = { getPreset, getPresets: async (user, filter) => { try { - const presets = await db.models.Preset.find({ ...filter, user }).lean(); + const presets = await Preset.find({ ...filter, user }).lean(); const defaultValue = 10000; presets.sort((a, b) => { @@ -39,7 +38,6 @@ module.exports = { const setter = { $set: {} }; const { user: _, ...cleanPreset } = preset; const update = { presetId, ...cleanPreset }; - const Preset = db.models.Preset; if (preset.tools && Array.isArray(preset.tools)) { update.tools = preset.tools @@ -77,7 +75,7 @@ module.exports = { deletePresets: async (user, filter) => { // let toRemove = await Preset.find({ ...filter, user }).select('presetId'); // const ids = toRemove.map((instance) => instance.presetId); - let deleteCount = await db.models.Preset.deleteMany({ ...filter, user }); + let deleteCount = await Preset.deleteMany({ ...filter, user }); return deleteCount; }, }; diff --git a/api/models/Project.js b/api/models/Project.js index 1c39239f36..51c5286a0b 100644 --- a/api/models/Project.js +++ b/api/models/Project.js @@ -1,5 +1,5 @@ +const { Project } = require('@librechat/data-schemas'); const { GLOBAL_PROJECT_NAME } = require('librechat-data-provider').Constants; -const db = require('~/lib/db/connectDb'); /** * Retrieve a project by ID and convert the found project document to a plain object. @@ -9,7 +9,7 @@ const db = require('~/lib/db/connectDb'); * @returns {Promise} A plain object representing the project document, or `null` if no project is found. */ const getProjectById = async function (projectId, fieldsToSelect = null) { - const query = db.models.Project.findById(projectId); + const query = Project.findById(projectId); if (fieldsToSelect) { query.select(fieldsToSelect); @@ -36,7 +36,7 @@ const getProjectByName = async function (projectName, fieldsToSelect = null) { select: fieldsToSelect, }; - return await db.models.Project.findOneAndUpdate(query, update, options); + return await Project.findOneAndUpdate(query, update, options); }; /** @@ -47,7 +47,7 @@ const getProjectByName = async function (projectName, fieldsToSelect = null) { * @returns {Promise} The updated project document. */ const addGroupIdsToProject = async function (projectId, promptGroupIds) { - return await db.models.Project.findByIdAndUpdate( + return await Project.findByIdAndUpdate( projectId, { $addToSet: { promptGroupIds: { $each: promptGroupIds } } }, { new: true }, @@ -62,7 +62,7 @@ const addGroupIdsToProject = async function (projectId, promptGroupIds) { * @returns {Promise} The updated project document. */ const removeGroupIdsFromProject = async function (projectId, promptGroupIds) { - return await db.models.Project.findByIdAndUpdate( + return await Project.findByIdAndUpdate( projectId, { $pull: { promptGroupIds: { $in: promptGroupIds } } }, { new: true }, @@ -76,7 +76,7 @@ const removeGroupIdsFromProject = async function (projectId, promptGroupIds) { * @returns {Promise} */ const removeGroupFromAllProjects = async (promptGroupId) => { - await db.models.Project.updateMany({}, { $pull: { promptGroupIds: promptGroupId } }); + await Project.updateMany({}, { $pull: { promptGroupIds: promptGroupId } }); }; /** @@ -87,7 +87,7 @@ const removeGroupFromAllProjects = async (promptGroupId) => { * @returns {Promise} The updated project document. */ const addAgentIdsToProject = async function (projectId, agentIds) { - return await db.models.Project.findByIdAndUpdate( + return await Project.findByIdAndUpdate( projectId, { $addToSet: { agentIds: { $each: agentIds } } }, { new: true }, @@ -102,7 +102,7 @@ const addAgentIdsToProject = async function (projectId, agentIds) { * @returns {Promise} The updated project document. */ const removeAgentIdsFromProject = async function (projectId, agentIds) { - return await db.models.Project.findByIdAndUpdate( + return await Project.findByIdAndUpdate( projectId, { $pull: { agentIds: { $in: agentIds } } }, { new: true }, @@ -116,7 +116,7 @@ const removeAgentIdsFromProject = async function (projectId, agentIds) { * @returns {Promise} */ const removeAgentFromAllProjects = async (agentId) => { - await db.models.Project.updateMany({}, { $pull: { agentIds: agentId } }); + await Project.updateMany({}, { $pull: { agentIds: agentId } }); }; module.exports = { diff --git a/api/models/Prompt.js b/api/models/Prompt.js index cd159b93fe..68c91d880b 100644 --- a/api/models/Prompt.js +++ b/api/models/Prompt.js @@ -1,4 +1,5 @@ const { ObjectId } = require('mongodb'); +const { Prompt, PromptGroup, logger } = require('@librechat/data-schemas'); const { SystemRoles, SystemCategories, Constants } = require('librechat-data-provider'); const { getProjectByName, @@ -7,8 +8,6 @@ const { removeGroupFromAllProjects, } = require('./Project'); const { escapeRegExp } = require('~/server/utils'); -const { logger } = require('~/config'); -const db = require('~/lib/db/connectDb'); /** * Create a pipeline for the aggregation to get prompt groups @@ -133,7 +132,7 @@ const getAllPromptGroups = async (req, filter) => { } const promptGroupsPipeline = createAllGroupsPipeline(combinedQuery); - return await db.models.PromptGroup.aggregate(promptGroupsPipeline).exec(); + return await PromptGroup.aggregate(promptGroupsPipeline).exec(); } catch (error) { console.error('Error getting all prompt groups', error); return { message: 'Error getting all prompt groups' }; @@ -233,7 +232,7 @@ const deletePromptGroup = async ({ _id, author, role }) => { throw new Error('Prompt group not found'); } - await db.models.Prompt.deleteMany(groupQuery); + await Prompt.deleteMany(groupQuery); await removeGroupFromAllProjects(_id); return { message: 'Prompt group deleted successfully' }; }; @@ -250,7 +249,6 @@ module.exports = { createPromptGroup: async (saveData) => { try { const { prompt, group, author, authorName } = saveData; - const { Prompt, PromptGroup } = db.models; let newPromptGroup = await PromptGroup.findOneAndUpdate( { ...group, author, authorName, productionId: null }, @@ -306,7 +304,6 @@ module.exports = { /** @type {TPrompt} */ let newPrompt; - const { Prompt } = db.models; try { newPrompt = await Prompt.create(newPromptData); } catch (error) { @@ -326,7 +323,7 @@ module.exports = { }, getPrompts: async (filter) => { try { - return await db.models.Prompt.find(filter).sort({ createdAt: -1 }).lean(); + return await Prompt.find(filter).sort({ createdAt: -1 }).lean(); } catch (error) { logger.error('Error getting prompts', error); return { message: 'Error getting prompts' }; @@ -337,7 +334,7 @@ module.exports = { if (filter.groupId) { filter.groupId = new ObjectId(filter.groupId); } - return await db.models.Prompt.findOne(filter).lean(); + return await Prompt.findOne(filter).lean(); } catch (error) { logger.error('Error getting prompt', error); return { message: 'Error getting prompt' }; @@ -350,7 +347,7 @@ module.exports = { */ getRandomPromptGroups: async (filter) => { try { - const result = await db.models.PromptGroup.aggregate([ + const result = await PromptGroup.aggregate([ { $match: { category: { $ne: '' }, @@ -383,7 +380,7 @@ module.exports = { }, getPromptGroupsWithPrompts: async (filter) => { try { - return await db.models.PromptGroup.findOne(filter) + return await PromptGroup.findOne(filter) .populate({ path: 'prompts', select: '-_id -__v -user', @@ -397,7 +394,7 @@ module.exports = { }, getPromptGroup: async (filter) => { try { - return await db.models.PromptGroup.findOne(filter).lean(); + return await PromptGroup.findOne(filter).lean(); } catch (error) { logger.error('Error getting prompt group', error); return { message: 'Error getting prompt group' }; @@ -418,7 +415,6 @@ module.exports = { */ deletePrompt: async ({ promptId, groupId, author, role }) => { const query = { _id: promptId, groupId, author }; - const { Prompt, PromptGroup } = db.models; if (role === SystemRoles.ADMIN) { delete query.author; } @@ -483,7 +479,7 @@ module.exports = { } const updateData = { ...data, ...updateOps }; - const updatedDoc = await db.models.PromptGroup.findOneAndUpdate(filter, updateData, { + const updatedDoc = await PromptGroup.findOneAndUpdate(filter, updateData, { new: true, upsert: false, }); @@ -505,7 +501,6 @@ module.exports = { */ makePromptProduction: async (promptId) => { try { - const { Prompt, PromptGroup } = db.models; const prompt = await Prompt.findById(promptId).lean(); if (!prompt) { @@ -530,7 +525,7 @@ module.exports = { }, updatePromptLabels: async (_id, labels) => { try { - const response = await db.models.Prompt.updateOne({ _id }, { $set: { labels } }); + const response = await Prompt.updateOne({ _id }, { $set: { labels } }); if (response.matchedCount === 0) { return { message: 'Prompt not found' }; } diff --git a/api/models/Role.js b/api/models/Role.js index b337968add..f91d6fafb2 100644 --- a/api/models/Role.js +++ b/api/models/Role.js @@ -6,9 +6,8 @@ const { permissionsSchema, removeNullishValues, } = require('librechat-data-provider'); +const { Role, logger } = require('@librechat/data-schemas'); const getLogStores = require('~/cache/getLogStores'); -const { logger } = require('~/config'); -const db = require('~/lib/db/connectDb'); /** * Retrieve a role by name and convert the found role document to a plain object. @@ -21,7 +20,6 @@ const db = require('~/lib/db/connectDb'); */ const getRoleByName = async function (roleName, fieldsToSelect = null) { const cache = getLogStores(CacheKeys.ROLES); - const { Role } = db.models; try { const cachedRole = await cache.get(roleName); if (cachedRole) { @@ -55,7 +53,7 @@ const getRoleByName = async function (roleName, fieldsToSelect = null) { const updateRoleByName = async function (roleName, updates) { const cache = getLogStores(CacheKeys.ROLES); try { - const role = await db.models.Role.findOneAndUpdate( + const role = await Role.findOneAndUpdate( { name: roleName }, { $set: updates }, { new: true, lean: true }, @@ -76,7 +74,6 @@ const updateRoleByName = async function (roleName, updates) { * @param {Object.>} permissionsUpdate - Permissions to update and their values. */ async function updateAccessPermissions(roleName, permissionsUpdate) { - const { Role } = db.models; // Filter and clean the permission updates based on our schema definition. const updates = {}; for (const [permissionType, permissions] of Object.entries(permissionsUpdate)) { @@ -180,7 +177,6 @@ async function updateAccessPermissions(roleName, permissionsUpdate) { * @returns {Promise} */ const initializeRoles = async function () { - const { Role } = db.models; for (const roleName of [SystemRoles.ADMIN, SystemRoles.USER]) { let role = await Role.findOne({ name: roleName }); const defaultPerms = roleDefaults[roleName].permissions; @@ -210,7 +206,6 @@ const initializeRoles = async function () { * @returns {Promise} Number of roles migrated. */ const migrateRoleSchema = async function (roleName) { - const { Role } = db.models; try { // Get roles to migrate let roles; diff --git a/api/models/Role.spec.js b/api/models/Role.spec.js index 0b5431dfa4..736ca881c8 100644 --- a/api/models/Role.spec.js +++ b/api/models/Role.spec.js @@ -1,4 +1,5 @@ const mongoose = require('mongoose'); +const { Role, logger } = require('@librechat/data-schemas'); const { MongoMemoryServer } = require('mongodb-memory-server'); const { SystemRoles, @@ -9,8 +10,6 @@ const { const { getRoleByName, updateAccessPermissions, initializeRoles } = require('~/models/Role'); const getLogStores = require('~/cache/getLogStores'); -const db = require('~/lib/db/connectDb'); - // Mock the cache jest.mock('~/cache/getLogStores', () => jest.fn().mockReturnValue({ @@ -21,14 +20,11 @@ jest.mock('~/cache/getLogStores', () => ); let mongoServer; -let Role; beforeAll(async () => { mongoServer = await MongoMemoryServer.create(); const mongoUri = mongoServer.getUri(); - await db.connectDb(mongoUri); - - Role = db.models.Role; + await mongoose.connect(mongoUri); }); afterAll(async () => { diff --git a/api/models/Share.js b/api/models/Share.js index 441dfa25d6..fe03fc60f1 100644 --- a/api/models/Share.js +++ b/api/models/Share.js @@ -1,8 +1,7 @@ const { nanoid } = require('nanoid'); const { Constants } = require('librechat-data-provider'); -const db = require('~/lib/db/connectDb'); +const { SharedLink, Conversation, logger } = require('@librechat/data-schemas'); const { getMessages } = require('./Message'); -const logger = require('~/config/winston'); class ShareServiceError extends Error { constructor(message, code) { @@ -73,7 +72,7 @@ function anonymizeMessages(messages, newConvoId) { async function getSharedMessages(shareId) { try { - const share = await db.models.SharedLink.findOne({ shareId, isPublic: true }) + const share = await SharedLink.findOne({ shareId, isPublic: true }) .populate({ path: 'messages', select: '-_id -__v -user', @@ -148,7 +147,7 @@ async function getSharedLinks(user, pageParam, pageSize, isPublic, sortBy, sortD query.conversationId = { $in: query.conversationId }; } - const sharedLinks = await db.models.SharedLink.find(query) + const sharedLinks = await SharedLink.find(query) .sort(sort) .limit(pageSize + 1) .select('-__v -user') @@ -181,7 +180,7 @@ async function getSharedLinks(user, pageParam, pageSize, isPublic, sortBy, sortD async function deleteAllSharedLinks(user) { try { - const result = await db.models.SharedLink.deleteMany({ user }); + const result = await SharedLink.deleteMany({ user }); return { message: 'All shared links deleted successfully', deletedCount: result.deletedCount, @@ -199,7 +198,6 @@ async function createSharedLink(user, conversationId) { if (!user || !conversationId) { throw new ShareServiceError('Missing required parameters', 'INVALID_PARAMS'); } - const { SharedLink, Conversation } = db.models; try { const [existingShare, conversationMessages] = await Promise.all([ SharedLink.findOne({ conversationId, isPublic: true }).select('-_id -__v -user').lean(), @@ -241,7 +239,7 @@ async function getSharedLink(user, conversationId) { } try { - const share = await db.models.SharedLink.findOne({ conversationId, user, isPublic: true }) + const share = await SharedLink.findOne({ conversationId, user, isPublic: true }) .select('shareId -_id') .lean(); @@ -265,7 +263,6 @@ async function updateSharedLink(user, shareId) { throw new ShareServiceError('Missing required parameters', 'INVALID_PARAMS'); } - const { SharedLink } = db.models; try { const share = await SharedLink.findOne({ shareId }).select('-_id -__v -user').lean(); @@ -316,7 +313,7 @@ async function deleteSharedLink(user, shareId) { } try { - const result = await db.models.SharedLink.findOneAndDelete({ shareId, user }).lean(); + const result = await SharedLink.findOneAndDelete({ shareId, user }).lean(); if (!result) { return null; diff --git a/api/models/Token.js b/api/models/Token.js index ddbafc71cf..e5a2332d6b 100644 --- a/api/models/Token.js +++ b/api/models/Token.js @@ -1,5 +1,5 @@ +const { Token } = require('@librechat/data-schemas'); const { encryptV2 } = require('~/server/utils/crypto'); -const db = require('~/lib/db/connectDb'); /** * Handles the OAuth token by creating or updating the token. @@ -29,7 +29,6 @@ async function handleOAuthToken({ expiresIn: parseInt(expiresIn, 10) || 3600, }; - const { Token } = db.models; const existingToken = await Token.findToken({ userId, identifier }); if (existingToken) { return await Token.updateToken({ identifier }, tokenData); diff --git a/api/models/ToolCall.js b/api/models/ToolCall.js index 9df2f2f846..a587767e9c 100644 --- a/api/models/ToolCall.js +++ b/api/models/ToolCall.js @@ -1,4 +1,4 @@ -const db = require('~/lib/db/connectDb'); +const { ToolCall } = require('@librechat/data-schemas'); /** * Create a new tool call * @param {IToolCallData} toolCallData - The tool call data @@ -6,7 +6,7 @@ const db = require('~/lib/db/connectDb'); */ async function createToolCall(toolCallData) { try { - return await db.models.ToolCall.create(toolCallData); + return await ToolCall.create(toolCallData); } catch (error) { throw new Error(`Error creating tool call: ${error.message}`); } @@ -19,7 +19,7 @@ async function createToolCall(toolCallData) { */ async function getToolCallById(id) { try { - return await db.models.ToolCall.findById(id).lean(); + return await ToolCall.findById(id).lean(); } catch (error) { throw new Error(`Error fetching tool call: ${error.message}`); } @@ -33,7 +33,7 @@ async function getToolCallById(id) { */ async function getToolCallsByMessage(messageId, userId) { try { - return await db.models.ToolCall.find({ messageId, user: userId }).lean(); + return await ToolCall.find({ messageId, user: userId }).lean(); } catch (error) { throw new Error(`Error fetching tool calls: ${error.message}`); } @@ -47,7 +47,7 @@ async function getToolCallsByMessage(messageId, userId) { */ async function getToolCallsByConvo(conversationId, userId) { try { - return await db.models.ToolCall.find({ conversationId, user: userId }).lean(); + return await ToolCall.find({ conversationId, user: userId }).lean(); } catch (error) { throw new Error(`Error fetching tool calls: ${error.message}`); } @@ -61,7 +61,7 @@ async function getToolCallsByConvo(conversationId, userId) { */ async function updateToolCall(id, updateData) { try { - return await db.models.ToolCall.findByIdAndUpdate(id, updateData, { new: true }).lean(); + return await ToolCall.findByIdAndUpdate(id, updateData, { new: true }).lean(); } catch (error) { throw new Error(`Error updating tool call: ${error.message}`); } @@ -79,7 +79,7 @@ async function deleteToolCalls(userId, conversationId) { if (conversationId) { query.conversationId = conversationId; } - return await db.models.ToolCall.deleteMany(query); + return await ToolCall.deleteMany(query); } catch (error) { throw new Error(`Error deleting tool call: ${error.message}`); } diff --git a/api/models/Transaction.js b/api/models/Transaction.js index a28b2a475f..3a955071c6 100644 --- a/api/models/Transaction.js +++ b/api/models/Transaction.js @@ -1,9 +1,7 @@ const mongoose = require('mongoose'); -const { transactionSchema } = require('@librechat/data-schemas'); +const { Balance, Transaction, logger } = require('@librechat/data-schemas'); const { getBalanceConfig } = require('~/server/services/Config'); const { getMultiplier, getCacheMultiplier } = require('./tx'); -const { logger } = require('~/config'); -const db = require('~/lib/db/connectDb'); const cancelRate = 1.15; @@ -23,7 +21,6 @@ const updateBalance = async ({ user, incrementValue, setValues }) => { let maxRetries = 10; // Number of times to retry on conflict let delay = 50; // Initial retry delay in ms let lastError = null; - const { Balance } = db.models; for (let attempt = 1; attempt <= maxRetries; attempt++) { let currentBalanceDoc; @@ -165,7 +162,6 @@ function calculateTokenValue(txn) { * @returns {Promise} - The created transaction. */ async function createAutoRefillTransaction(txData) { - const Transaction = db.models.Transaction; if (txData.rawAmount != null && isNaN(txData.rawAmount)) { return; } @@ -198,7 +194,7 @@ async function createTransaction(txData) { return; } - const transaction = new db.models.Transaction(txData); + const transaction = new Transaction(txData); transaction.endpointTokenConfig = txData.endpointTokenConfig; calculateTokenValue(transaction); @@ -228,7 +224,7 @@ async function createTransaction(txData) { * @param {txData} txData - Transaction data. */ async function createStructuredTransaction(txData) { - const transaction = new db.models.Transaction({ + const transaction = new Transaction({ ...txData, endpointTokenConfig: txData.endpointTokenConfig, }); @@ -329,7 +325,7 @@ function calculateStructuredTokenValue(txn) { */ async function getTransactions(filter) { try { - return await db.models.Transaction.find(filter).lean(); + return await Transaction.find(filter).lean(); } catch (error) { logger.error('Error querying transactions:', error); throw error; diff --git a/api/models/Transaction.spec.js b/api/models/Transaction.spec.js index 008566e2fa..ab19b4179d 100644 --- a/api/models/Transaction.spec.js +++ b/api/models/Transaction.spec.js @@ -1,24 +1,19 @@ const mongoose = require('mongoose'); +const { Balance } = require('@librechat/data-schemas'); const { MongoMemoryServer } = require('mongodb-memory-server'); const { spendTokens, spendStructuredTokens } = require('./spendTokens'); const { getBalanceConfig } = require('~/server/services/Config'); const { getMultiplier, getCacheMultiplier } = require('./tx'); -const db = require('~/lib/db/connectDb'); const { createTransaction } = require('./Transaction'); // Mock the custom config module so we can control the balance flag. jest.mock('~/server/services/Config'); let mongoServer; -let Balance; -let Transaction; beforeAll(async () => { mongoServer = await MongoMemoryServer.create(); const mongoUri = mongoServer.getUri(); - await db.connectDb(mongoUri); - - Balance = db.models.Balance; - Transaction = db.models.Transaction; + await mongoose.connect(mongoUri); }); afterAll(async () => { diff --git a/api/models/balanceMethods.js b/api/models/balanceMethods.js index e262cfcfe2..ec82c35bf6 100644 --- a/api/models/balanceMethods.js +++ b/api/models/balanceMethods.js @@ -1,9 +1,9 @@ +const { Balance } = require('@librechat/data-schemas'); const { ViolationTypes } = require('librechat-data-provider'); const { createAutoRefillTransaction } = require('./Transaction'); const { logViolation } = require('~/cache'); const { getMultiplier } = require('./tx'); const { logger } = require('~/config'); -const db = require('~/lib/db/connectDb'); function isInvalidDate(date) { return isNaN(date); @@ -26,7 +26,7 @@ const checkBalanceRecord = async function ({ const tokenCost = amount * multiplier; // Retrieve the balance record - let record = await db.models.Balance.findOne({ user }).lean(); + let record = await Balance.findOne({ user }).lean(); if (!record) { logger.debug('[Balance.check] No balance record found for user', { user }); return { diff --git a/api/models/convoStructure.spec.js b/api/models/convoStructure.spec.js index 0720bf3495..e78f9ce84d 100644 --- a/api/models/convoStructure.spec.js +++ b/api/models/convoStructure.spec.js @@ -1,7 +1,7 @@ const mongoose = require('mongoose'); +const { Message } = require('@librechat/data-schemas'); const { MongoMemoryServer } = require('mongodb-memory-server'); const { getMessages, bulkSaveMessages } = require('./Message'); -const db = require('~/lib/db/connectDb'); // Original version of buildTree function function buildTree({ messages, fileMap }) { @@ -43,13 +43,10 @@ function buildTree({ messages, fileMap }) { } let mongod; -let Message; beforeAll(async () => { mongod = await MongoMemoryServer.create(); const uri = mongod.getUri(); - await db.connectDb(uri); - - Message = db.models.Message; + await mongoose.connect(uri); }); afterAll(async () => { diff --git a/api/models/inviteUser.js b/api/models/inviteUser.js index a7519aa76e..b8b4792339 100644 --- a/api/models/inviteUser.js +++ b/api/models/inviteUser.js @@ -1,7 +1,6 @@ const mongoose = require('mongoose'); +const { Token, logger } = require('@librechat/data-schemas'); const { getRandomValues, hashToken } = require('~/server/utils/crypto'); -const logger = require('~/config/winston'); -const db = require('~/lib/db/connectDb'); /** * @module inviteUser @@ -23,7 +22,7 @@ const createInvite = async (email) => { const fakeUserId = new mongoose.Types.ObjectId(); - await db.models.Token.createToken({ + await Token.createToken({ userId: fakeUserId, email, token: hash, @@ -50,7 +49,7 @@ const getInvite = async (encodedToken, email) => { try { const token = decodeURIComponent(encodedToken); const hash = await hashToken(token); - const invite = await db.models.Token.findToken({ token: hash, email }); + const invite = await Token.findToken({ token: hash, email }); if (!invite) { throw new Error('Invite not found or email does not match'); diff --git a/api/models/spendTokens.spec.js b/api/models/spendTokens.spec.js index a17f752944..7c9d804e25 100644 --- a/api/models/spendTokens.spec.js +++ b/api/models/spendTokens.spec.js @@ -1,7 +1,7 @@ const mongoose = require('mongoose'); const { MongoMemoryServer } = require('mongodb-memory-server'); +const { Transaction, Balance } = require('@librechat/data-schemas'); const { spendTokens, spendStructuredTokens } = require('./spendTokens'); -const db = require('~/lib/db/connectDb'); const { createTransaction, createAutoRefillTransaction } = require('./Transaction'); // Mock the logger to prevent console output during tests @@ -20,15 +20,9 @@ describe('spendTokens', () => { let mongoServer; let userId; - let Transaction; - let Balance; beforeAll(async () => { mongoServer = await MongoMemoryServer.create(); - const mongoUri = mongoServer.getUri(); - await db.connectDb(mongoUri); - - Balance = db.models.Balance; - Transaction = db.models.Transaction; + await mongoose.connect(mongoServer.getUri()); }); afterAll(async () => { diff --git a/api/server/controllers/AuthController.js b/api/server/controllers/AuthController.js index 15633091df..47db8d13a1 100644 --- a/api/server/controllers/AuthController.js +++ b/api/server/controllers/AuthController.js @@ -1,6 +1,7 @@ -const openIdClient = require('openid-client'); const cookies = require('cookie'); const jwt = require('jsonwebtoken'); +const openIdClient = require('openid-client'); +const { User, Session, logger } = require('@librechat/data-schemas'); const { registerUser, resetPassword, @@ -9,9 +10,7 @@ const { setOpenIDAuthTokens, } = require('~/server/services/AuthService'); const { getOpenIdConfig } = require('~/strategies'); -const { logger } = require('~/config'); const { isEnabled } = require('~/server/utils'); -const db = require('~/lib/db/connectDb'); const registrationController = async (req, res) => { try { @@ -48,7 +47,7 @@ const resetPasswordController = async (req, res) => { if (resetPasswordService instanceof Error) { return res.status(400).json(resetPasswordService); } else { - await db.models.Session.deleteAllUserSessions({ userId: req.body.userId }); + await Session.deleteAllUserSessions({ userId: req.body.userId }); return res.status(200).json(resetPasswordService); } } catch (e) { @@ -70,7 +69,7 @@ const refreshController = async (req, res) => { const openIdConfig = getOpenIdConfig(); const tokenset = await openIdClient.refreshTokenGrant(openIdConfig, refreshToken); const claims = tokenset.claims(); - const user = await findUser({ email: claims.email }); + const user = await User.findUser({ email: claims.email }); if (!user) { return res.status(401).redirect('/login'); } @@ -83,7 +82,7 @@ const refreshController = async (req, res) => { } try { const payload = jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET); - const user = await db.models.User.getUserById(payload.id, '-password -__v -totpSecret'); + const user = await User.getUserById(payload.id, '-password -__v -totpSecret'); if (!user) { return res.status(401).redirect('/login'); } @@ -96,7 +95,7 @@ const refreshController = async (req, res) => { } // Find the session with the hashed refresh token - const session = await db.models.Session.findSession({ + const session = await Session.findSession({ userId: userId, refreshToken: refreshToken, }); diff --git a/api/server/controllers/Balance.js b/api/server/controllers/Balance.js index 62e721b597..7a96f1d657 100644 --- a/api/server/controllers/Balance.js +++ b/api/server/controllers/Balance.js @@ -1,6 +1,6 @@ -const db = require('~/lib/db/connectDb'); +const { Balance } = require('@librechat/data-schemas'); async function balanceController(req, res) { - const balanceData = await db.models.Balance.findOne( + const balanceData = await Balance.findOne( { user: req.user.id }, '-_id tokenCredits autoRefillEnabled refillIntervalValue refillIntervalUnit lastRefill refillAmount', ).lean(); diff --git a/api/server/controllers/TwoFactorController.js b/api/server/controllers/TwoFactorController.js index 2f481a32d3..1ca6bd70b5 100644 --- a/api/server/controllers/TwoFactorController.js +++ b/api/server/controllers/TwoFactorController.js @@ -1,3 +1,4 @@ +const { User, logger } = require('@librechat/data-schemas'); const { generateTOTPSecret, generateBackupCodes, @@ -5,9 +6,7 @@ const { verifyBackupCode, getTOTPSecret, } = require('~/server/services/twoFactorService'); -const { logger } = require('~/config'); const { encryptV3 } = require('~/server/utils/crypto'); -const db = require('~/lib/db/connectDb'); const safeAppTitle = (process.env.APP_TITLE || 'LibreChat').replace(/\s+/g, ''); /** @@ -24,7 +23,7 @@ const enable2FA = async (req, res) => { const encryptedSecret = encryptV3(secret); // Update the user record: store the secret & backup codes and set twoFactorEnabled to false. - const user = await db.models.User.updateUser(userId, { + const user = await User.updateUser(userId, { totpSecret: encryptedSecret, backupCodes: codeObjects, twoFactorEnabled: false, @@ -46,7 +45,7 @@ const verify2FA = async (req, res) => { try { const userId = req.user.id; const { token, backupCode } = req.body; - const user = await db.models.User.getUserById(userId); + const user = await User.getUserById(userId); if (!user || !user.totpSecret) { return res.status(400).json({ message: '2FA not initiated' }); @@ -78,7 +77,6 @@ const confirm2FA = async (req, res) => { try { const userId = req.user.id; const { token } = req.body; - const { User } = db.models; const user = await User.getUserById(userId); if (!user || !user.totpSecret) { @@ -103,7 +101,7 @@ const confirm2FA = async (req, res) => { const disable2FA = async (req, res) => { try { const userId = req.user.id; - await db.models.User.updateUser(userId, { totpSecret: null, backupCodes: [], twoFactorEnabled: false }); + await User.updateUser(userId, { totpSecret: null, backupCodes: [], twoFactorEnabled: false }); return res.status(200).json(); } catch (err) { logger.error('[disable2FA]', err); @@ -118,7 +116,7 @@ const regenerateBackupCodes = async (req, res) => { try { const userId = req.user.id; const { plainCodes, codeObjects } = await generateBackupCodes(); - await db.models.User.updateUser(userId, { backupCodes: codeObjects }); + await User.updateUser(userId, { backupCodes: codeObjects }); return res.status(200).json({ backupCodes: plainCodes, backupCodesHash: codeObjects, diff --git a/api/server/controllers/UserController.js b/api/server/controllers/UserController.js index 80ede8e7f4..c49b4b944a 100644 --- a/api/server/controllers/UserController.js +++ b/api/server/controllers/UserController.js @@ -1,18 +1,11 @@ const { Tools, - Constants, FileSources, webSearchKeys, extractWebSearchEnvVars, } = require('librechat-data-provider'); -const { - Balance, - getFiles, - deleteFiles, - deleteConvos, - deletePresets, - deleteMessages, -} = require('~/models'); +const { User, Session, Transaction, Balance, logger } = require('@librechat/data-schemas'); +const { getFiles, deleteFiles, deleteConvos, deletePresets, deleteMessages } = require('~/models'); const { updateUserPluginAuth, deleteUserPluginAuth } = require('~/server/services/PluginService'); const { updateUserPluginsService, deleteUserKey } = require('~/server/services/UserService'); const { verifyEmail, resendVerificationEmail } = require('~/server/services/AuthService'); @@ -20,9 +13,6 @@ const { needsRefresh, getNewS3URL } = require('~/server/services/Files/S3/crud') const { processDeleteRequest } = require('~/server/services/Files/process'); const { deleteAllSharedLinks } = require('~/models/Share'); const { deleteToolCalls } = require('~/models/ToolCall'); -const { Transaction } = require('~/models/Transaction'); -const { logger } = require('~/config'); -const db = require('~/lib/db/connectDb'); const getUserController = async (req, res) => { /** @type {MongoUser} */ @@ -36,7 +26,7 @@ const getUserController = async (req, res) => { const originalAvatar = userData.avatar; try { userData.avatar = await getNewS3URL(userData.avatar); - await db.models.User.updateUser(userData.id, { avatar: userData.avatar }); + await User.updateUser(userData.id, { avatar: userData.avatar }); } catch (error) { userData.avatar = originalAvatar; logger.error('Error getting new S3 URL for avatar:', error); @@ -47,7 +37,7 @@ const getUserController = async (req, res) => { const getTermsStatusController = async (req, res) => { try { - const user = await db.models.User.findById(req.user.id); + const user = await User.findById(req.user.id); if (!user) { return res.status(404).json({ message: 'User not found' }); } @@ -60,7 +50,7 @@ const getTermsStatusController = async (req, res) => { const acceptTermsController = async (req, res) => { try { - const user = await db.models.User.findByIdAndUpdate(req.user.id, { termsAccepted: true }, { new: true }); + const user = await User.findByIdAndUpdate(req.user.id, { termsAccepted: true }, { new: true }); if (!user) { return res.status(404).json({ message: 'User not found' }); } @@ -157,7 +147,7 @@ const deleteUserController = async (req, res) => { try { await deleteMessages({ user: user.id }); // delete user messages - await db.models.Session.deleteAllUserSessions({ userId: user.id }); // delete user sessions + await Session.deleteAllUserSessions({ userId: user.id }); // delete user sessions await Transaction.deleteMany({ user: user.id }); // delete user transactions await deleteUserKey({ userId: user.id, all: true }); // delete user keys await Balance.deleteMany({ user: user._id }); // delete user balances @@ -165,7 +155,7 @@ const deleteUserController = async (req, res) => { /* TODO: Delete Assistant Threads */ await deleteConvos(user.id); // delete user convos await deleteUserPluginAuth(user.id, null, true); // delete user plugin auth - await db.models.User.deleteUserById(user.id); // delete user + await User.deleteUserById(user.id); // delete user await deleteAllSharedLinks(user.id); // delete user shared links await deleteUserFiles(req); // delete user files await deleteFiles(null, user.id); // delete database files in case of orphaned files from previous steps diff --git a/api/server/controllers/auth/TwoFactorAuthController.js b/api/server/controllers/auth/TwoFactorAuthController.js index 150124b0cc..ae7bf870c8 100644 --- a/api/server/controllers/auth/TwoFactorAuthController.js +++ b/api/server/controllers/auth/TwoFactorAuthController.js @@ -5,8 +5,7 @@ const { getTOTPSecret, } = require('~/server/services/twoFactorService'); const { setAuthTokens } = require('~/server/services/AuthService'); -const { logger } = require('~/config'); -const db = require('~/lib/db/connectDb'); +const { User, logger } = require('@librechat/data-schemas'); /** * Verifies the 2FA code during login using a temporary token. @@ -25,7 +24,7 @@ const verify2FAWithTempToken = async (req, res) => { return res.status(401).json({ message: 'Invalid or expired temporary token' }); } - const user = await db.models.User.getUserById(payload.userId); + const user = await User.getUserById(payload.userId); if (!user || !user.twoFactorEnabled) { return res.status(400).json({ message: '2FA is not enabled for this user' }); } diff --git a/api/server/middleware/checkBan.js b/api/server/middleware/checkBan.js index cafd0c5f74..8741075597 100644 --- a/api/server/middleware/checkBan.js +++ b/api/server/middleware/checkBan.js @@ -5,9 +5,7 @@ const { isEnabled, removePorts } = require('~/server/utils'); const keyvMongo = require('~/cache/keyvMongo'); const denyRequest = require('./denyRequest'); const { getLogStores } = require('~/cache'); -const { logger } = require('~/config'); -const db = require('~/lib/db/connectDb'); - +const { User, logger } = require('@librechat/data-schemas'); const banCache = new Keyv({ store: keyvMongo, namespace: ViolationTypes.BAN, ttl: 0 }); const message = 'Your account has been temporarily banned due to violations of our service.'; @@ -58,7 +56,7 @@ const checkBan = async (req, res, next = () => {}) => { let userId = req.user?.id ?? req.user?._id ?? null; if (!userId && req?.body?.email) { - const user = await db.models.User.findUser({ email: req.body.email }, '_id'); + const user = await User.findUser({ email: req.body.email }, '_id'); userId = user?._id ? user._id.toString() : userId; } diff --git a/api/server/middleware/checkInviteUser.js b/api/server/middleware/checkInviteUser.js index 17586d534d..1174fc2ed8 100644 --- a/api/server/middleware/checkInviteUser.js +++ b/api/server/middleware/checkInviteUser.js @@ -1,5 +1,5 @@ +const { deleteTokens } = require('@librechat/data-schemas'); const { getInvite } = require('~/models/inviteUser'); -const db = require('~/lib/db/connectDb'); async function checkInviteUser(req, res, next) { const token = req.body.token; @@ -16,7 +16,7 @@ async function checkInviteUser(req, res, next) { return res.status(400).json({ message: 'Invalid invite token' }); } - await db.models.Token.deleteTokens({ token: invite.token }); + await deleteTokens({ token: invite.token }); req.invite = invite; next(); } catch (error) { diff --git a/api/server/middleware/setBalanceConfig.js b/api/server/middleware/setBalanceConfig.js index b2874c4bef..1b1cd79f39 100644 --- a/api/server/middleware/setBalanceConfig.js +++ b/api/server/middleware/setBalanceConfig.js @@ -1,6 +1,5 @@ +const { Balance, logger } = require('@librechat/data-schemas'); const { getBalanceConfig } = require('~/server/services/Config'); -const { logger } = require('~/config'); -const db = require('~/lib/db/connectDb'); /** * Middleware to synchronize user balance settings with current balance configuration. @@ -20,14 +19,14 @@ const setBalanceConfig = async (req, res, next) => { } const userId = req.user._id; - const userBalanceRecord = await db.models.Balance.findOne({ user: userId }).lean(); + const userBalanceRecord = await Balance.findOne({ user: userId }).lean(); const updateFields = buildUpdateFields(balanceConfig, userBalanceRecord); if (Object.keys(updateFields).length === 0) { return next(); } - await db.models.Balance.findOneAndUpdate( + await Balance.findOneAndUpdate( { user: userId }, { $set: updateFields }, { upsert: true, new: true }, diff --git a/api/server/routes/messages.js b/api/server/routes/messages.js index 232550865d..08fd273a02 100644 --- a/api/server/routes/messages.js +++ b/api/server/routes/messages.js @@ -1,5 +1,6 @@ const express = require('express'); const { ContentTypes } = require('librechat-data-provider'); +const { Message, logger } = require('@librechat/data-schemas'); const { saveConvo, saveMessage, @@ -13,8 +14,6 @@ const { requireJwtAuth, validateMessageReq } = require('~/server/middleware'); const { cleanUpPrimaryKeyValue } = require('~/lib/utils/misc'); const { getConvosQueried } = require('~/models/Conversation'); const { countTokens } = require('~/server/utils'); -const { logger } = require('~/config'); -const db = require('~/lib/db/connectDb'); const router = express.Router(); router.use(requireJwtAuth); @@ -40,7 +39,7 @@ router.get('/', async (req, res) => { const sortOrder = sortDirection === 'asc' ? 1 : -1; if (conversationId && messageId) { - const message = await db.models.Message.findOne({ + const message = await Message.findOne({ conversationId, messageId, user: user, @@ -51,14 +50,14 @@ router.get('/', async (req, res) => { if (cursor) { filter[sortField] = sortOrder === 1 ? { $gt: cursor } : { $lt: cursor }; } - const messages = await db.models.Message.find(filter) + const messages = await Message.find(filter) .sort({ [sortField]: sortOrder }) .limit(pageSize + 1) .lean(); const nextCursor = messages.length > pageSize ? messages.pop()[sortField] : null; response = { messages, nextCursor }; } else if (search) { - const searchResults = await db.models.Message.meiliSearch(search, undefined, true); + const searchResults = await Message.meiliSearch(search, undefined, true); const messages = searchResults.hits || []; diff --git a/api/server/services/ActionService.js b/api/server/services/ActionService.js index 9dfc9d8712..e9ff02f5b7 100644 --- a/api/server/services/ActionService.js +++ b/api/server/services/ActionService.js @@ -1,6 +1,7 @@ const jwt = require('jsonwebtoken'); const { nanoid } = require('nanoid'); const { tool } = require('@langchain/core/tools'); +const { findToken } = require('@librechat/data-schemas'); const { GraphEvents, sleep } = require('@librechat/agents'); const { Time, @@ -231,10 +232,9 @@ async function createActionTool({ }; const tokenPromises = []; - const { Token } = db.models; - tokenPromises.push(Token.findToken({ userId, type: 'oauth', identifier })); + tokenPromises.push(findToken({ userId, type: 'oauth', identifier })); tokenPromises.push( - Token.findToken({ + findToken({ userId, type: 'oauth_refresh', identifier: `${identifier}:refresh`, diff --git a/api/server/services/Files/Azure/images.js b/api/server/services/Files/Azure/images.js index a4d2d0932e..d3950bedcc 100644 --- a/api/server/services/Files/Azure/images.js +++ b/api/server/services/Files/Azure/images.js @@ -1,11 +1,11 @@ const fs = require('fs'); const path = require('path'); const sharp = require('sharp'); +const { updateUser, logger } = require('@librechat/data-schemas'); const { resizeImageBuffer } = require('../images/resize'); const { updateFile } = require('~/models/File'); -const { logger } = require('~/config'); const { saveBufferToAzure } = require('./crud'); -const db = require('~/lib/db/connectDb'); + /** * Uploads an image file to Azure Blob Storage. * It resizes and converts the image similar to your Firebase implementation. @@ -107,7 +107,7 @@ async function processAzureAvatar({ buffer, userId, manual, basePath = 'images', const isManual = manual === 'true'; const url = `${downloadURL}?manual=${isManual}`; if (isManual) { - await db.models?.User.updateUser(userId, { avatar: url }); + await updateUser(userId, { avatar: url }); } return url; } catch (error) { diff --git a/api/server/services/Files/Firebase/images.js b/api/server/services/Files/Firebase/images.js index 57c5026683..8002a8998e 100644 --- a/api/server/services/Files/Firebase/images.js +++ b/api/server/services/Files/Firebase/images.js @@ -4,8 +4,7 @@ const sharp = require('sharp'); const { resizeImageBuffer } = require('../images/resize'); const { saveBufferToFirebase } = require('./crud'); const { updateFile } = require('~/models/File'); -const { logger } = require('~/config'); -const db = require('~/lib/db/connectDb'); +const { logger, updateUser } = require('@librechat/data-schemas'); /** * Converts an image file to the target format. The function first resizes the image based on the specified @@ -99,7 +98,7 @@ async function processFirebaseAvatar({ buffer, userId, manual }) { const url = `${downloadURL}?manual=${isManual}`; if (isManual) { - await db.models.User.updateUser(userId, { avatar: url }); + await updateUser(userId, { avatar: url }); } return url; diff --git a/api/server/services/Files/Local/images.js b/api/server/services/Files/Local/images.js index 520075efe5..ebfe8bc898 100644 --- a/api/server/services/Files/Local/images.js +++ b/api/server/services/Files/Local/images.js @@ -1,9 +1,9 @@ const fs = require('fs'); const path = require('path'); const sharp = require('sharp'); +const { updateUser } = require('@librechat/data-schemas'); const { resizeImageBuffer } = require('../images/resize'); const { updateFile } = require('~/models/File'); -const db = require('~/lib/db/connectDb'); /** * Converts an image file to the target format. The function first resizes the image based on the specified @@ -141,7 +141,7 @@ async function processLocalAvatar({ buffer, userId, manual }) { let url = `${urlRoute}?manual=${isManual}`; if (isManual) { - await db.models?.User.updateUser(userId, { avatar: url }); + await updateUser(userId, { avatar: url }); } return url; diff --git a/api/server/services/Files/S3/images.js b/api/server/services/Files/S3/images.js index aeaf984c44..b358a6f015 100644 --- a/api/server/services/Files/S3/images.js +++ b/api/server/services/Files/S3/images.js @@ -1,11 +1,10 @@ const fs = require('fs'); const path = require('path'); const sharp = require('sharp'); +const { logger, updateUser } = require('@librechat/data-schemas'); const { resizeImageBuffer } = require('../images/resize'); -const { saveBufferToS3 } = require('./crud'); const { updateFile } = require('~/models/File'); -const { logger } = require('~/config'); -const db = require('~/lib/db/connectDb'); +const { saveBufferToS3 } = require('./crud'); const defaultBasePath = 'images'; @@ -102,7 +101,7 @@ async function processS3Avatar({ buffer, userId, manual, basePath = defaultBaseP try { const downloadURL = await saveBufferToS3({ userId, buffer, fileName: 'avatar.png', basePath }); if (manual === 'true') { - await db.models?.User.updateUser(userId, { avatar: downloadURL }); + await updateUser(userId, { avatar: downloadURL }); } return downloadURL; } catch (error) { diff --git a/api/server/services/UserService.js b/api/server/services/UserService.js index d8525ac368..8b2847c04a 100644 --- a/api/server/services/UserService.js +++ b/api/server/services/UserService.js @@ -1,7 +1,7 @@ const { ErrorTypes } = require('librechat-data-provider'); -const { encrypt, decrypt } = require('~/server/utils'); -const { logger } = require('~/config'); -const db = require('~/lib/db/connectDb'); +const { Key, logger, updateUser } = require('@librechat/data-schemas'); +const { encrypt, decrypt } = require('~/server/utils/crypto'); + /** * Updates the plugins for a user based on the action specified (install/uninstall). * @async @@ -16,11 +16,10 @@ const db = require('~/lib/db/connectDb'); const updateUserPluginsService = async (user, pluginKey, action) => { try { const userPlugins = user.plugins || []; - const { User } = db.models; if (action === 'install') { - return await User.updateUser(user._id, { plugins: [...userPlugins, pluginKey] }); + return await updateUser(user._id, { plugins: [...userPlugins, pluginKey] }); } else if (action === 'uninstall') { - return await User.updateUser(user._id, { + return await updateUser(user._id, { plugins: userPlugins.filter((plugin) => plugin !== pluginKey), }); } @@ -42,7 +41,7 @@ const updateUserPluginsService = async (user, pluginKey, action) => { * an error indicating that there is no user key available. */ const getUserKey = async ({ userId, name }) => { - const keyValue = await db.models.Key.findOne({ userId, name }).lean(); + const keyValue = await Key.findOne({ userId, name }).lean(); if (!keyValue) { throw new Error( JSON.stringify({ @@ -89,7 +88,7 @@ const getUserKeyValues = async ({ userId, name }) => { * returns its expiry date. If the key is not found, it returns null for the expiry date. */ const getUserKeyExpiry = async ({ userId, name }) => { - const keyValue = await db.models.Key.findOne({ userId, name }).lean(); + const keyValue = await Key.findOne({ userId, name }).lean(); if (!keyValue) { return { expiresAt: null }; } @@ -123,7 +122,7 @@ const updateUserKey = async ({ userId, name, value, expiresAt = null }) => { // make sure to remove if already present updateQuery.$unset = { expiresAt }; } - return await db.models.Key.findOneAndUpdate({ userId, name }, updateQuery, { + return await Key.findOneAndUpdate({ userId, name }, updateQuery, { upsert: true, new: true, }).lean(); @@ -143,10 +142,10 @@ const updateUserKey = async ({ userId, name, value, expiresAt = null }) => { */ const deleteUserKey = async ({ userId, name, all = false }) => { if (all) { - return await db.models.Key.deleteMany({ userId }); + return await Key.deleteMany({ userId }); } - await db.models.Key.findOneAndDelete({ userId, name }).lean(); + await Key.findOneAndDelete({ userId, name }).lean(); }; /** diff --git a/api/server/services/twoFactorService.js b/api/server/services/twoFactorService.js index 68c335b390..7029eb75e7 100644 --- a/api/server/services/twoFactorService.js +++ b/api/server/services/twoFactorService.js @@ -1,7 +1,6 @@ const { webcrypto } = require('node:crypto'); -const { decryptV3, decryptV2 } = require('../utils/crypto'); -const { hashBackupCode } = require('~/server/utils/crypto'); -const db = require('~/lib/db/connectDb'); +const { User } = require('@librechat/data-schemas'); +const { hashBackupCode, decryptV3, decryptV2 } = require('~/server/utils/crypto'); // Base32 alphabet for TOTP secret encoding. const BASE32_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'; @@ -173,7 +172,7 @@ const verifyBackupCode = async ({ user, backupCode }) => { : codeObj, ); // Update the user record with the marked backup code. - await db.models.User.updateUser(user._id, { backupCodes: updatedBackupCodes }); + await User.updateUser(user._id, { backupCodes: updatedBackupCodes }); return true; } return false; diff --git a/api/strategies/appleStrategy.test.js b/api/strategies/appleStrategy.test.js index d1083224e7..887b4f5a38 100644 --- a/api/strategies/appleStrategy.test.js +++ b/api/strategies/appleStrategy.test.js @@ -3,10 +3,9 @@ const { MongoMemoryServer } = require('mongodb-memory-server'); const jwt = require('jsonwebtoken'); const { Strategy: AppleStrategy } = require('passport-apple'); const socialLogin = require('./socialLogin'); -const { logger } = require('~/config'); const { createSocialUser, handleExistingUser } = require('./process'); const { isEnabled } = require('~/server/utils'); -const db = require('~/lib/db/connectDb'); +const { User, logger } = require('@librechat/data-schemas'); // Mocking external dependencies jest.mock('jsonwebtoken'); @@ -29,14 +28,11 @@ describe('Apple Login Strategy', () => { let appleStrategyInstance; const OLD_ENV = process.env; let getProfileDetails; - let User; // Start and stop in-memory MongoDB beforeAll(async () => { mongoServer = await MongoMemoryServer.create(); const mongoUri = mongoServer.getUri(); - await db.connectDb(mongoUri); - - User = db.models.User; + await mongoose.connect(mongoUri); }); afterAll(async () => { diff --git a/api/strategies/jwtStrategy.js b/api/strategies/jwtStrategy.js index fd4d556970..91dac3875d 100644 --- a/api/strategies/jwtStrategy.js +++ b/api/strategies/jwtStrategy.js @@ -1,7 +1,6 @@ const { SystemRoles } = require('librechat-data-provider'); +const { User, logger } = require('@librechat/data-schemas'); const { Strategy: JwtStrategy, ExtractJwt } = require('passport-jwt'); -const { logger } = require('~/config'); -const db = require('~/lib/db/connectDb'); // JWT strategy const jwtLogin = () => @@ -12,7 +11,6 @@ const jwtLogin = () => }, async (payload, done) => { try { - const {User} = db.models; const user = await User.getUserById(payload?.id, '-password -__v -totpSecret'); if (user) { user.id = user._id.toString(); diff --git a/api/strategies/ldapStrategy.js b/api/strategies/ldapStrategy.js index 7f688723e4..ee2a80a4d9 100644 --- a/api/strategies/ldapStrategy.js +++ b/api/strategies/ldapStrategy.js @@ -1,10 +1,10 @@ const fs = require('fs'); const LdapStrategy = require('passport-ldapauth'); const { SystemRoles } = require('librechat-data-provider'); +const { User, createUser, findUser, updateUser } = require('@librechat/data-schemas'); +const { getBalanceConfig } = require('~/server/services/Config'); const { isEnabled } = require('~/server/utils'); const logger = require('~/utils/logger'); -const db = require('~/lib/db/connectDb'); -const { getBalanceConfig } = require('~/server/services/Config'); const { LDAP_URL, @@ -81,7 +81,6 @@ const ldapOptions = { }; const ldapLogin = new LdapStrategy(ldapOptions, async (userinfo, done) => { - const { User } = db.models; if (!userinfo) { return done(null, false, { message: 'Invalid credentials' }); } @@ -90,7 +89,7 @@ const ldapLogin = new LdapStrategy(ldapOptions, async (userinfo, done) => { const ldapId = (LDAP_ID && userinfo[LDAP_ID]) || userinfo.uid || userinfo.sAMAccountName || userinfo.mail; - let user = await User.findUser({ ldapId }); + let user = await findUser({ ldapId }); const fullNameAttributes = LDAP_FULL_NAME && LDAP_FULL_NAME.split(','); const fullName = @@ -126,8 +125,7 @@ const ldapLogin = new LdapStrategy(ldapOptions, async (userinfo, done) => { role: isFirstRegisteredUser ? SystemRoles.ADMIN : SystemRoles.USER, }; const balanceConfig = await getBalanceConfig(); - - const userId = await User.createUser(user, balanceConfig); + const userId = await createUser(user, balanceConfig); user._id = userId; } else { // Users registered in LDAP are assumed to have their user information managed in LDAP, @@ -139,7 +137,7 @@ const ldapLogin = new LdapStrategy(ldapOptions, async (userinfo, done) => { user.name = fullName; } - user = await User.updateUser(user._id, user); + user = await updateUser(user._id, user); done(null, user); } catch (err) { logger.error('[ldapStrategy]', err); diff --git a/api/strategies/localStrategy.js b/api/strategies/localStrategy.js index 814c66bf89..70d03de419 100644 --- a/api/strategies/localStrategy.js +++ b/api/strategies/localStrategy.js @@ -1,10 +1,9 @@ +const { User, logger } = require('@librechat/data-schemas'); const { errorsToString } = require('librechat-data-provider'); const { Strategy: PassportLocalStrategy } = require('passport-local'); -const { comparePassword } = require('~/models'); const { isEnabled, checkEmailConfig } = require('~/server/utils'); +const { comparePassword } = require('~/models'); const { loginSchema } = require('./validators'); -const logger = require('~/utils/logger'); -const db = require('~/lib/db/connectDb'); // Unix timestamp for 2024-06-07 15:20:18 Eastern Time const verificationEnabledTimestamp = 1717788018; @@ -15,7 +14,6 @@ async function validateLoginRequest(req) { } async function passportLogin(req, email, password, done) { - const {User} = db.models; try { const validationError = await validateLoginRequest(req); if (validationError) { diff --git a/api/strategies/openidStrategy.js b/api/strategies/openidStrategy.js index f3852e036b..0ebe8a73d7 100644 --- a/api/strategies/openidStrategy.js +++ b/api/strategies/openidStrategy.js @@ -1,14 +1,14 @@ -const { CacheKeys } = require('librechat-data-provider'); const fetch = require('node-fetch'); const passport = require('passport'); -const jwtDecode = require('jsonwebtoken/decode'); -const { HttpsProxyAgent } = require('https-proxy-agent'); const client = require('openid-client'); +const jwtDecode = require('jsonwebtoken/decode'); +const { CacheKeys } = require('librechat-data-provider'); +const { HttpsProxyAgent } = require('https-proxy-agent'); +const { User, logger } = require('@librechat/data-schemas'); const { Strategy: OpenIDStrategy } = require('openid-client/passport'); const { getStrategyFunctions } = require('~/server/services/Files/strategies'); -const { isEnabled } = require('~/server/utils'); -const { logger } = require('~/config'); const getLogStores = require('~/cache/getLogStores'); +const { isEnabled } = require('~/server/utils'); /** * @typedef {import('openid-client').ClientMetadata} ClientMetadata @@ -212,7 +212,6 @@ function convertToUsername(input, defaultValue = '') { * @throws {Error} If an error occurs during the setup process. */ async function setupOpenId() { - const { User } = db.models; try { /** @type {ClientMetadata} */ const clientMetadata = { diff --git a/api/strategies/openidStrategy.spec.js b/api/strategies/openidStrategy.spec.js index 68e7aff49a..e96dfe1083 100644 --- a/api/strategies/openidStrategy.spec.js +++ b/api/strategies/openidStrategy.spec.js @@ -9,18 +9,6 @@ const mockFindUser = jest.fn(); const mockUpdateUser = jest.fn(); let User; -jest.mock('@librechat/data-schemas', () => { - return { - registerModels: jest.fn().mockReturnValue({ - User: { - createUser: mockCreateUser, - findUser: mockFindUser, - updateUser: mockUpdateUser, - }, - }), - }; -}); - const mockModels = { User: { createUser: mockCreateUser, diff --git a/api/strategies/process.js b/api/strategies/process.js index a7b7699682..c8dee199b6 100644 --- a/api/strategies/process.js +++ b/api/strategies/process.js @@ -1,7 +1,7 @@ const { FileSources } = require('librechat-data-provider'); +const { updateUser, createUser, getUserById } = require('@librechat/data-schemas'); const { getStrategyFunctions } = require('~/server/services/Files/strategies'); const { resizeAvatar } = require('~/server/services/Files/images/avatar'); -const db = require('~/lib/db/connectDb'); const { getBalanceConfig } = require('~/server/services/Config'); /** @@ -35,7 +35,7 @@ const handleExistingUser = async (oldUser, avatarUrl) => { } if (updatedAvatar) { - await db.models.User.updateUser(oldUser._id, { avatar: updatedAvatar }); + await updateUser(oldUser._id, { avatar: updatedAvatar }); } }; @@ -80,7 +80,7 @@ const createSocialUser = async ({ }; const balanceConfig = await getBalanceConfig(); - const newUserId = await db.models.User.createUser(update, balanceConfig); + const newUserId = await createUser(update, balanceConfig); const fileStrategy = process.env.CDN_PROVIDER; const isLocal = fileStrategy === FileSources.local; @@ -91,10 +91,10 @@ const createSocialUser = async ({ }); const { processAvatar } = getStrategyFunctions(fileStrategy); const avatar = await processAvatar({ buffer: resizedBuffer, userId: newUserId }); - await User.updateUser(newUserId, { avatar }); + await updateUser(newUserId, { avatar }); } - return await User.getUserById(newUserId); + return await getUserById(newUserId); }; module.exports = { diff --git a/api/strategies/socialLogin.js b/api/strategies/socialLogin.js index ffe0a0b82c..c61913ceef 100644 --- a/api/strategies/socialLogin.js +++ b/api/strategies/socialLogin.js @@ -1,7 +1,6 @@ +const { findUser, logger } = require('@librechat/data-schemas'); const { createSocialUser, handleExistingUser } = require('./process'); const { isEnabled } = require('~/server/utils'); -const { logger } = require('~/config'); -const db = require('~/lib/db/connectDb'); const socialLogin = (provider, getProfileDetails) => async (accessToken, refreshToken, idToken, profile, cb) => { @@ -11,7 +10,7 @@ const socialLogin = profile, }); - const oldUser = await db.models.User.findUser({ email: email.trim() }); + const oldUser = await findUser({ email: email.trim() }); const ALLOW_SOCIAL_REGISTRATION = isEnabled(process.env.ALLOW_SOCIAL_REGISTRATION); if (oldUser) { diff --git a/config/add-balance.js b/config/add-balance.js index d775f0c12d..31e6ade09d 100644 --- a/config/add-balance.js +++ b/config/add-balance.js @@ -1,9 +1,9 @@ const path = require('path'); +const { User } = require('@librechat/data-schemas'); require('module-alias')({ base: path.resolve(__dirname, '..', 'api') }); const { askQuestion, silentExit } = require('./helpers'); const { isEnabled } = require('~/server/utils/handleText'); const { createTransaction } = require('~/models/Transaction'); -const db = require('~/lib/db/connectDb'); const connect = require('./connect'); (async () => { @@ -65,7 +65,7 @@ const connect = require('./connect'); } // Validate the user - const user = await db.models.User.findOne({ email }).lean(); + const user = await User.findOne({ email }).lean(); if (!user) { console.red('Error: No user with that email was found!'); silentExit(1); diff --git a/config/ban-user.js b/config/ban-user.js index 711dc9a25d..e7991dd31d 100644 --- a/config/ban-user.js +++ b/config/ban-user.js @@ -1,8 +1,8 @@ const path = require('path'); +const { User } = require('@librechat/data-schemas'); require('module-alias')({ base: path.resolve(__dirname, '..', 'api') }); const { askQuestion, silentExit } = require('./helpers'); const banViolation = require('~/cache/banViolation'); -const db = require('~/lib/db/connectDb'); const connect = require('./connect'); (async () => { @@ -44,7 +44,7 @@ const connect = require('./connect'); silentExit(1); } - const user = await db.models.User.findOne({ email }).lean(); + const user = await User.findOne({ email }).lean(); if (!user) { console.red('Error: No user with that email was found!'); silentExit(1); diff --git a/config/create-user.js b/config/create-user.js index cd00455b15..7bc5ad771f 100644 --- a/config/create-user.js +++ b/config/create-user.js @@ -1,8 +1,8 @@ const path = require('path'); +const { User } = require('@librechat/data-schemas'); require('module-alias')({ base: path.resolve(__dirname, '..', 'api') }); const { registerUser } = require('~/server/services/AuthService'); const { askQuestion, silentExit } = require('./helpers'); -const db = require('~/lib/db/connectDb'); const connect = require('./connect'); (async () => { @@ -92,7 +92,7 @@ or the user will need to attempt logging in to have a verification link sent to } } - const userExists = await db.models.User.findOne({ $or: [{ email }, { username }] }); + const userExists = await User.findOne({ $or: [{ email }, { username }] }); if (userExists) { console.red('Error: A user with that email or username already exists!'); silentExit(1); diff --git a/config/delete-user.js b/config/delete-user.js index 8044c74090..5d2327fb72 100644 --- a/config/delete-user.js +++ b/config/delete-user.js @@ -1,7 +1,7 @@ const path = require('path'); +const { User } = require('@librechat/data-schemas'); require('module-alias')({ base: path.resolve(__dirname, '..', 'api') }); const { askQuestion, silentExit } = require('./helpers'); -const db = require('~/lib/db/connectDb'); const connect = require('./connect'); (async () => { @@ -20,10 +20,10 @@ const connect = require('./connect'); } else { email = await askQuestion('Email:'); } - let user = await db.models.User.findOne({ email: email }); + let user = await User.findOne({ email: email }); if (user !== null) { if ((await askQuestion(`Delete user ${user}?`)) === 'y') { - user = await db.models.User.findOneAndDelete({ _id: user._id }); + user = await User.findOneAndDelete({ _id: user._id }); if (user !== null) { console.yellow(`Deleted user ${user}`); } else { diff --git a/config/invite-user.js b/config/invite-user.js index ad92cdefec..0666d00655 100644 --- a/config/invite-user.js +++ b/config/invite-user.js @@ -1,9 +1,9 @@ const path = require('path'); +const { User } = require('@librechat/data-schemas'); require('module-alias')({ base: path.resolve(__dirname, '..', 'api') }); const { sendEmail, checkEmailConfig } = require('~/server/utils'); const { askQuestion, silentExit } = require('./helpers'); const { createInvite } = require('~/models/inviteUser'); -const db = require('~/lib/db/connectDb'); const connect = require('./connect'); (async () => { @@ -40,7 +40,7 @@ const connect = require('./connect'); } // Check if the user already exists - const userExists = await db.models.User.findOne({ email }); + const userExists = await User.findOne({ email }); if (userExists) { console.red('Error: A user with that email already exists!'); silentExit(1); diff --git a/config/list-balances.js b/config/list-balances.js index 1b446d4e4e..d9f090cc9f 100644 --- a/config/list-balances.js +++ b/config/list-balances.js @@ -1,8 +1,7 @@ const path = require('path'); +const { User, Balance } = require('@librechat/data-schemas'); require('module-alias')({ base: path.resolve(__dirname, '..', 'api') }); const { silentExit } = require('./helpers'); -const Balance = require('~/models/Balance'); -const db = require('~/lib/db/connectDb'); const connect = require('./connect'); (async () => { @@ -15,7 +14,7 @@ const connect = require('./connect'); console.purple('Show the balance of all users'); console.purple('-----------------------------'); - let users = await db.models.User.find({}); + let users = await User.find({}); for (const user of users) { let balance = await Balance.findOne({ user: user._id }); if (balance !== null) { diff --git a/config/list-users.js b/config/list-users.js index f97b20629f..58ecf7caeb 100644 --- a/config/list-users.js +++ b/config/list-users.js @@ -1,12 +1,12 @@ const path = require('path'); +const { User } = require('@librechat/data-schemas'); require('module-alias')({ base: path.resolve(__dirname, '..', 'api') }); const connect = require('./connect'); -const db = require('~/lib/db/connectDb'); const listUsers = async () => { try { await connect(); - const users = await db.models.User.find({}, 'email provider avatar username name createdAt'); + const users = await User.find({}, 'email provider avatar username name createdAt'); console.log('\nUser List:'); console.log('----------------------------------------'); diff --git a/config/reset-terms.js b/config/reset-terms.js index 69c3e50802..ad3872e416 100644 --- a/config/reset-terms.js +++ b/config/reset-terms.js @@ -1,8 +1,8 @@ const path = require('path'); +const { User } = require('@librechat/data-schemas'); require('module-alias')({ base: path.resolve(__dirname, '..', 'api') }); -const db = require('~/lib/db/connectDb'); -const connect = require('./connect'); const { askQuestion, silentExit } = require('./helpers'); +const connect = require('./connect'); (async () => { await connect(); @@ -20,7 +20,7 @@ const { askQuestion, silentExit } = require('./helpers'); } try { - const result = await db.models.User.updateMany({}, { $set: { termsAccepted: false } }); + const result = await User.updateMany({}, { $set: { termsAccepted: false } }); console.green(`Updated ${result.modifiedCount} user(s).`); } catch (error) { console.red('Error resetting terms acceptance:', error); diff --git a/config/set-balance.js b/config/set-balance.js index 4fb7c74723..6dcff04fcc 100644 --- a/config/set-balance.js +++ b/config/set-balance.js @@ -1,8 +1,8 @@ const path = require('path'); +const { User, Balance } = require('@librechat/data-schemas'); require('module-alias')({ base: path.resolve(__dirname, '..', 'api') }); const { askQuestion, silentExit } = require('./helpers'); const { isEnabled } = require('~/server/utils/handleText'); -const db = require('~/lib/db/connectDb'); const connect = require('./connect'); (async () => { @@ -56,7 +56,7 @@ const connect = require('./connect'); } // Validate the user - const user = await db.models.User.findOne({ email }).lean(); + const user = await User.findOne({ email }).lean(); if (!user) { console.red('Error: No user with that email was found!'); silentExit(1); @@ -64,7 +64,7 @@ const connect = require('./connect'); console.purple(`Found user: ${user.email}`); } - let balance = await db.models.Balance.findOne({ user: user._id }).lean(); + let balance = await Balance.findOne({ user: user._id }).lean(); if (!balance) { console.purple('User has no balance!'); } else { @@ -85,7 +85,7 @@ const connect = require('./connect'); */ let result; try { - result = await db.models.Balance.findOneAndUpdate( + result = await Balance.findOneAndUpdate( { user: user._id }, { tokenCredits: amount }, { upsert: true, new: true }, diff --git a/config/update-banner.js b/config/update-banner.js index 6e3a254ef0..2c4fcba0c0 100644 --- a/config/update-banner.js +++ b/config/update-banner.js @@ -1,9 +1,9 @@ const path = require('path'); const { v5: uuidv5 } = require('uuid'); +const { Banner } = require('@librechat/data-schemas'); require('module-alias')({ base: path.resolve(__dirname, '..', 'api') }); const { askQuestion, askMultiLineQuestion, silentExit } = require('./helpers'); const connect = require('./connect'); -const db = require('~/lib/db/connectDb'); (async () => { await connect(); @@ -87,7 +87,6 @@ const db = require('~/lib/db/connectDb'); let result; try { - const { Banner } = db.models; // There is always only one Banner record in the DB. // If a Banner exists in the DB, it will be updated. // If it doesn't exist, a new one will be added. diff --git a/config/user-stats.js b/config/user-stats.js index f37b1dabe9..d6b3c27b98 100644 --- a/config/user-stats.js +++ b/config/user-stats.js @@ -1,7 +1,7 @@ const path = require('path'); require('module-alias')({ base: path.resolve(__dirname, '..', 'api') }); const { silentExit } = require('./helpers'); -const db = require('~/lib/db/connectDb'); +const { User, Conversation, Message } = require('@librechat/data-schemas'); const connect = require('./connect'); (async () => { @@ -14,7 +14,6 @@ const connect = require('./connect'); console.purple('Show the stats of all users'); console.purple('-----------------------------'); - const { User, Conversation, Message } = db.models; let users = await User.find({}); let userData = []; for (const user of users) { diff --git a/packages/data-schemas/src/index.ts b/packages/data-schemas/src/index.ts index 4a2d68b505..f5f8c865da 100644 --- a/packages/data-schemas/src/index.ts +++ b/packages/data-schemas/src/index.ts @@ -1,10 +1,7 @@ -// Export all types +export { default as logger } from './config/winston'; +export { default as meiliLogger } from './config/meiliLogger'; export * from './types'; - -// Export all models export * from './models'; - -// Export all methods export * from './methods'; // Export schemas (if needed for direct access) @@ -16,13 +13,10 @@ export { default as tokenSchema } from './schema/token'; export { signPayload, hashToken } from './schema/session'; export { default as actionSchema } from './schema/action'; -export type { IAction } from './schema/action'; export { default as agentSchema } from './schema/agent'; -export type { IAgent } from './schema/agent'; export { default as assistantSchema } from './schema/assistant'; -export type { IAssistant } from './schema/assistant'; export { default as balanceSchema } from './schema/balance'; @@ -38,10 +32,8 @@ export type { IConversationTag } from './schema/conversationTag'; export { default as convoSchema } from './schema/convo'; export { default as fileSchema } from './schema/file'; -export type { IMongoFile } from './schema/file'; export { default as keySchema } from './schema/key'; -export type { IKey } from './schema/key'; export { default as messageSchema } from './schema/message'; export type { IMessage } from './schema/message'; @@ -62,7 +54,6 @@ export { default as promptGroupSchema } from './schema/promptGroup'; export type { IPromptGroup, IPromptGroupDocument } from './schema/promptGroup'; export { default as roleSchema } from './schema/role'; -export type { IRole } from './schema/role'; export { default as shareSchema } from './schema/share'; export type { ISharedLink } from './schema/share'; diff --git a/packages/data-schemas/src/models/action.ts b/packages/data-schemas/src/models/action.ts new file mode 100644 index 0000000000..b42cda0d43 --- /dev/null +++ b/packages/data-schemas/src/models/action.ts @@ -0,0 +1,5 @@ +import mongoose from 'mongoose'; +import actionSchema from '~/schema/action'; +import type { IAction } from '~/types'; + +export const Action = mongoose.models.Action || mongoose.model('Action', actionSchema); diff --git a/packages/data-schemas/src/models/agent.ts b/packages/data-schemas/src/models/agent.ts new file mode 100644 index 0000000000..2aad2c3cf6 --- /dev/null +++ b/packages/data-schemas/src/models/agent.ts @@ -0,0 +1,5 @@ +import mongoose from 'mongoose'; +import agentSchema from '~/schema/agent'; +import type { IAgent } from '~/types'; + +export const Agent = mongoose.models.Agent || mongoose.model('Agent', agentSchema); diff --git a/packages/data-schemas/src/models/assistant.ts b/packages/data-schemas/src/models/assistant.ts new file mode 100644 index 0000000000..437eefd6e6 --- /dev/null +++ b/packages/data-schemas/src/models/assistant.ts @@ -0,0 +1,6 @@ +import mongoose from 'mongoose'; +import assistantSchema from '~/schema/assistant'; +import type { IAssistant } from '~/types'; + +export const Assistant = + mongoose.models.Assistant || mongoose.model('Assistant', assistantSchema); diff --git a/packages/data-schemas/src/models/banner.ts b/packages/data-schemas/src/models/banner.ts new file mode 100644 index 0000000000..625551e3f2 --- /dev/null +++ b/packages/data-schemas/src/models/banner.ts @@ -0,0 +1,5 @@ +import mongoose from 'mongoose'; +import bannerSchema from '~/schema/banner'; +import type { IBanner } from '~/types'; + +export const Banner = mongoose.models.Banner || mongoose.model('Banner', bannerSchema); diff --git a/packages/data-schemas/src/models/file.ts b/packages/data-schemas/src/models/file.ts new file mode 100644 index 0000000000..3fbc982ba1 --- /dev/null +++ b/packages/data-schemas/src/models/file.ts @@ -0,0 +1,5 @@ +import mongoose from 'mongoose'; +import fileSchema from '~/schema/file'; +import type { IMongoFile } from '~/types'; + +export const File = mongoose.models.File || mongoose.model('File', fileSchema); diff --git a/packages/data-schemas/src/models/index.ts b/packages/data-schemas/src/models/index.ts index fb094fd28e..febbf31691 100644 --- a/packages/data-schemas/src/models/index.ts +++ b/packages/data-schemas/src/models/index.ts @@ -5,3 +5,8 @@ export { Session } from './session'; export { Balance } from './balance'; export { Conversation } from './convo'; export { Message } from './message'; +export { Agent } from './agent'; +export { Role } from './role'; +export { Action } from './action'; +export { Assistant } from './assistant'; +export { File } from './file'; diff --git a/packages/data-schemas/src/models/plugins/mongoMeili.ts b/packages/data-schemas/src/models/plugins/mongoMeili.ts index 92cbb482f9..111f9e0bba 100644 --- a/packages/data-schemas/src/models/plugins/mongoMeili.ts +++ b/packages/data-schemas/src/models/plugins/mongoMeili.ts @@ -1,6 +1,6 @@ import _ from 'lodash'; -import mongoose, { Schema, Document, Model, Query } from 'mongoose'; import { MeiliSearch, Index } from 'meilisearch'; +import mongoose, { Schema, Document, Model, Query } from 'mongoose'; import logger from '~/config/meiliLogger'; interface MongoMeiliOptions { diff --git a/packages/data-schemas/src/models/role.ts b/packages/data-schemas/src/models/role.ts new file mode 100644 index 0000000000..07762cabd9 --- /dev/null +++ b/packages/data-schemas/src/models/role.ts @@ -0,0 +1,5 @@ +import mongoose from 'mongoose'; +import roleSchema from '~/schema/role'; +import type { IRole } from '~/types'; + +export const Role = mongoose.models.Role || mongoose.model('Role', roleSchema); diff --git a/packages/data-schemas/src/schema/action.ts b/packages/data-schemas/src/schema/action.ts index b47ea5f0fa..4d5f64a0e1 100644 --- a/packages/data-schemas/src/schema/action.ts +++ b/packages/data-schemas/src/schema/action.ts @@ -1,31 +1,5 @@ -import mongoose, { Schema, Document } from 'mongoose'; - -export interface IAction extends Document { - user: mongoose.Types.ObjectId; - action_id: string; - type: string; - settings?: unknown; - agent_id?: string; - assistant_id?: string; - metadata: { - api_key?: string; - auth: { - authorization_type?: string; - custom_auth_header?: string; - type: 'service_http' | 'oauth' | 'none'; - authorization_content_type?: string; - authorization_url?: string; - client_url?: string; - scope?: string; - token_exchange_method: 'default_post' | 'basic_auth_header' | null; - }; - domain: string; - privacy_policy_url?: string; - raw_spec?: string; - oauth_client_id?: string; - oauth_client_secret?: string; - }; -} +import mongoose, { Schema } from 'mongoose'; +import type { IAction } from '~/types'; // Define the Auth sub-schema with type-safety. const AuthSchema = new Schema( diff --git a/packages/data-schemas/src/schema/agent.ts b/packages/data-schemas/src/schema/agent.ts index 784c4f6c35..733645865c 100644 --- a/packages/data-schemas/src/schema/agent.ts +++ b/packages/data-schemas/src/schema/agent.ts @@ -1,33 +1,5 @@ -import { Schema, Document, Types } from 'mongoose'; -export interface IAgent extends Omit { - id: string; - name?: string; - description?: string; - instructions?: string; - avatar?: { - filepath: string; - source: string; - }; - provider: string; - model: string; - model_parameters?: Record; - artifacts?: string; - access_level?: number; - recursion_limit?: number; - tools?: string[]; - tool_kwargs?: Array; - actions?: string[]; - author: Types.ObjectId; - authorName?: string; - hide_sequential_outputs?: boolean; - end_after_tools?: boolean; - agent_ids?: string[]; - isCollaborative?: boolean; - conversation_starters?: string[]; - tool_resources?: unknown; - projectIds?: Types.ObjectId[]; - versions?: Omit[]; -} +import { Schema } from 'mongoose'; +import type { IAgent } from '~/types'; const agentSchema = new Schema( { diff --git a/packages/data-schemas/src/schema/assistant.ts b/packages/data-schemas/src/schema/assistant.ts index e58dc81b27..4f0226d38a 100644 --- a/packages/data-schemas/src/schema/assistant.ts +++ b/packages/data-schemas/src/schema/assistant.ts @@ -1,18 +1,5 @@ -import { Schema, Document, Types } from 'mongoose'; - -export interface IAssistant extends Document { - user: Types.ObjectId; - assistant_id: string; - avatar?: { - filepath: string; - source: string; - }; - conversation_starters?: string[]; - access_level?: number; - file_ids?: string[]; - actions?: string[]; - append_current_datetime?: boolean; -} +import { Schema } from 'mongoose'; +import type { IAssistant } from '~/types'; const assistantSchema = new Schema( { diff --git a/packages/data-schemas/src/schema/file.ts b/packages/data-schemas/src/schema/file.ts index 6d3b22a5a5..5bf4d95d87 100644 --- a/packages/data-schemas/src/schema/file.ts +++ b/packages/data-schemas/src/schema/file.ts @@ -1,32 +1,6 @@ -import mongoose, { Schema, Document, Types } from 'mongoose'; +import mongoose, { Schema } from 'mongoose'; import { FileSources } from 'librechat-data-provider'; - -// @ts-ignore -export interface IMongoFile extends Document { - user: Types.ObjectId; - conversationId?: string; - file_id: string; - temp_file_id?: string; - bytes: number; - text?: string; - filename: string; - filepath: string; - object: 'file'; - embedded?: boolean; - type: string; - context?: string; - usage: number; - source: string; - model?: string; - width?: number; - height?: number; - metadata?: { - fileIdentifier?: string; - }; - expiresAt?: Date; - createdAt?: Date; - updatedAt?: Date; -} +import type { IMongoFile } from '~/types'; const file: Schema = new Schema( { diff --git a/packages/data-schemas/src/schema/role.ts b/packages/data-schemas/src/schema/role.ts index 99a171a789..bd20fa4ca0 100644 --- a/packages/data-schemas/src/schema/role.ts +++ b/packages/data-schemas/src/schema/role.ts @@ -1,36 +1,6 @@ -import { Schema, Document } from 'mongoose'; +import { Schema } from 'mongoose'; import { PermissionTypes, Permissions } from 'librechat-data-provider'; - -export interface IRole extends Document { - name: string; - permissions: { - [PermissionTypes.BOOKMARKS]?: { - [Permissions.USE]?: boolean; - }; - [PermissionTypes.PROMPTS]?: { - [Permissions.SHARED_GLOBAL]?: boolean; - [Permissions.USE]?: boolean; - [Permissions.CREATE]?: boolean; - }; - [PermissionTypes.AGENTS]?: { - [Permissions.SHARED_GLOBAL]?: boolean; - [Permissions.USE]?: boolean; - [Permissions.CREATE]?: boolean; - }; - [PermissionTypes.MULTI_CONVO]?: { - [Permissions.USE]?: boolean; - }; - [PermissionTypes.TEMPORARY_CHAT]?: { - [Permissions.USE]?: boolean; - }; - [PermissionTypes.RUN_CODE]?: { - [Permissions.USE]?: boolean; - }; - [PermissionTypes.WEB_SEARCH]?: { - [Permissions.USE]?: boolean; - }; - }; -} +import type { IRole } from '~/types'; // Create a sub-schema for permissions. Notice we disable _id for this subdocument. const rolePermissionsSchema = new Schema( diff --git a/packages/data-schemas/src/types/action.ts b/packages/data-schemas/src/types/action.ts new file mode 100644 index 0000000000..6a269856dd --- /dev/null +++ b/packages/data-schemas/src/types/action.ts @@ -0,0 +1,28 @@ +import mongoose, { Document } from 'mongoose'; + +export interface IAction extends Document { + user: mongoose.Types.ObjectId; + action_id: string; + type: string; + settings?: unknown; + agent_id?: string; + assistant_id?: string; + metadata: { + api_key?: string; + auth: { + authorization_type?: string; + custom_auth_header?: string; + type: 'service_http' | 'oauth' | 'none'; + authorization_content_type?: string; + authorization_url?: string; + client_url?: string; + scope?: string; + token_exchange_method: 'default_post' | 'basic_auth_header' | null; + }; + domain: string; + privacy_policy_url?: string; + raw_spec?: string; + oauth_client_id?: string; + oauth_client_secret?: string; + }; +} diff --git a/packages/data-schemas/src/types/agent.ts b/packages/data-schemas/src/types/agent.ts new file mode 100644 index 0000000000..19b5a04cfa --- /dev/null +++ b/packages/data-schemas/src/types/agent.ts @@ -0,0 +1,31 @@ +import { Document, Types } from 'mongoose'; + +export interface IAgent extends Omit { + id: string; + name?: string; + description?: string; + instructions?: string; + avatar?: { + filepath: string; + source: string; + }; + provider: string; + model: string; + model_parameters?: Record; + artifacts?: string; + access_level?: number; + recursion_limit?: number; + tools?: string[]; + tool_kwargs?: Array; + actions?: string[]; + author: Types.ObjectId; + authorName?: string; + hide_sequential_outputs?: boolean; + end_after_tools?: boolean; + agent_ids?: string[]; + isCollaborative?: boolean; + conversation_starters?: string[]; + tool_resources?: unknown; + projectIds?: Types.ObjectId[]; + versions?: Omit[]; +} diff --git a/packages/data-schemas/src/types/assistant.ts b/packages/data-schemas/src/types/assistant.ts new file mode 100644 index 0000000000..d2e180c668 --- /dev/null +++ b/packages/data-schemas/src/types/assistant.ts @@ -0,0 +1,15 @@ +import { Document, Types } from 'mongoose'; + +export interface IAssistant extends Document { + user: Types.ObjectId; + assistant_id: string; + avatar?: { + filepath: string; + source: string; + }; + conversation_starters?: string[]; + access_level?: number; + file_ids?: string[]; + actions?: string[]; + append_current_datetime?: boolean; +} diff --git a/packages/data-schemas/src/types/banner.ts b/packages/data-schemas/src/types/banner.ts new file mode 100644 index 0000000000..288ae7e9f8 --- /dev/null +++ b/packages/data-schemas/src/types/banner.ts @@ -0,0 +1,10 @@ +import type { Document } from 'mongoose'; + +export interface IBanner extends Document { + bannerId: string; + message: string; + displayFrom: Date; + displayTo?: Date; + type: 'banner' | 'popup'; + isPublic: boolean; +} diff --git a/packages/data-schemas/src/types/file.ts b/packages/data-schemas/src/types/file.ts new file mode 100644 index 0000000000..231ab93332 --- /dev/null +++ b/packages/data-schemas/src/types/file.ts @@ -0,0 +1,27 @@ +import { Document, Types } from 'mongoose'; + +export interface IMongoFile extends Omit { + user: Types.ObjectId; + conversationId?: string; + file_id: string; + temp_file_id?: string; + bytes: number; + text?: string; + filename: string; + filepath: string; + object: 'file'; + embedded?: boolean; + type: string; + context?: string; + usage: number; + source: string; + model?: string; + width?: number; + height?: number; + metadata?: { + fileIdentifier?: string; + }; + expiresAt?: Date; + createdAt?: Date; + updatedAt?: Date; +} diff --git a/packages/data-schemas/src/types/index.ts b/packages/data-schemas/src/types/index.ts index b8538c2f73..27914d2721 100644 --- a/packages/data-schemas/src/types/index.ts +++ b/packages/data-schemas/src/types/index.ts @@ -3,4 +3,10 @@ export * from './token'; export * from './convo'; export * from './session'; export * from './balance'; +export * from './banner'; export * from './message'; +export * from './agent'; +export * from './role'; +export * from './action'; +export * from './assistant'; +export * from './file'; diff --git a/packages/data-schemas/src/types/role.ts b/packages/data-schemas/src/types/role.ts new file mode 100644 index 0000000000..773a1a3929 --- /dev/null +++ b/packages/data-schemas/src/types/role.ts @@ -0,0 +1,33 @@ +import { Document } from 'mongoose'; +import { PermissionTypes, Permissions } from 'librechat-data-provider'; + +export interface IRole extends Document { + name: string; + permissions: { + [PermissionTypes.BOOKMARKS]?: { + [Permissions.USE]?: boolean; + }; + [PermissionTypes.PROMPTS]?: { + [Permissions.SHARED_GLOBAL]?: boolean; + [Permissions.USE]?: boolean; + [Permissions.CREATE]?: boolean; + }; + [PermissionTypes.AGENTS]?: { + [Permissions.SHARED_GLOBAL]?: boolean; + [Permissions.USE]?: boolean; + [Permissions.CREATE]?: boolean; + }; + [PermissionTypes.MULTI_CONVO]?: { + [Permissions.USE]?: boolean; + }; + [PermissionTypes.TEMPORARY_CHAT]?: { + [Permissions.USE]?: boolean; + }; + [PermissionTypes.RUN_CODE]?: { + [Permissions.USE]?: boolean; + }; + [PermissionTypes.WEB_SEARCH]?: { + [Permissions.USE]?: boolean; + }; + }; +}