refactor: original changes

This commit is contained in:
Danny Avila 2025-05-30 04:28:22 -04:00
parent fa9177180f
commit f9c0e9853f
No known key found for this signature in database
GPG key ID: BF31EEB2C5CA0956
83 changed files with 413 additions and 505 deletions

View file

@ -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);

View file

@ -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);

View file

@ -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(),

View file

@ -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;
}

View file

@ -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();

View file

@ -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<Agent|null>} 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<Agent>} 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<void>} 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');

View file

@ -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 () => {

View file

@ -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<AssistantDocument|null>} 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<Array<AssistantDocument>>} 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<void>} 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 = {

View file

@ -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',

View file

@ -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);

View file

@ -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');

View file

@ -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<MongoFile>} 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<MongoFile>} 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<MongoFile>} 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);
};
/**

View file

@ -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;

View file

@ -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;
},
};

View file

@ -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<IMongoProject>} 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<IMongoProject>} 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<IMongoProject>} 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<void>}
*/
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<IMongoProject>} 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<IMongoProject>} 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<void>}
*/
const removeAgentFromAllProjects = async (agentId) => {
await db.models.Project.updateMany({}, { $pull: { agentIds: agentId } });
await Project.updateMany({}, { $pull: { agentIds: agentId } });
};
module.exports = {

View file

@ -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' };
}

View file

@ -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.<PermissionTypes, Object.<Permissions, boolean>>} 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<void>}
*/
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>} Number of roles migrated.
*/
const migrateRoleSchema = async function (roleName) {
const { Role } = db.models;
try {
// Get roles to migrate
let roles;

View file

@ -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 () => {

View file

@ -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;

View file

@ -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);

View file

@ -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}`);
}

View file

@ -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<object>} - 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;

View file

@ -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 () => {

View file

@ -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 {

View file

@ -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 () => {

View file

@ -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');

View file

@ -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 () => {

View file

@ -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,
});

View file

@ -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();

View file

@ -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,

View file

@ -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

View file

@ -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' });
}

View file

@ -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;
}

View file

@ -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) {

View file

@ -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 },

View file

@ -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 || [];

View file

@ -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`,

View file

@ -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) {

View file

@ -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;

View file

@ -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;

View file

@ -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) {

View file

@ -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();
};
/**

View file

@ -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;

View file

@ -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 () => {

View file

@ -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();

View file

@ -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);

View file

@ -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) {

View file

@ -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 = {

View file

@ -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,

View file

@ -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 = {

View file

@ -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) {

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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 {

View file

@ -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);

View file

@ -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) {

View file

@ -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('----------------------------------------');

View file

@ -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);

View file

@ -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 },

View file

@ -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.

View file

@ -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) {

View file

@ -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';

View file

@ -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<IAction>('Action', actionSchema);

View file

@ -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<IAgent>('Agent', agentSchema);

View file

@ -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<IAssistant>('Assistant', assistantSchema);

View file

@ -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<IBanner>('Banner', bannerSchema);

View file

@ -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<IMongoFile>('File', fileSchema);

View file

@ -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';

View file

@ -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 {

View file

@ -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<IRole>('Role', roleSchema);

View file

@ -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(

View file

@ -1,33 +1,5 @@
import { Schema, Document, Types } from 'mongoose';
export interface IAgent extends Omit<Document, 'model'> {
id: string;
name?: string;
description?: string;
instructions?: string;
avatar?: {
filepath: string;
source: string;
};
provider: string;
model: string;
model_parameters?: Record<string, unknown>;
artifacts?: string;
access_level?: number;
recursion_limit?: number;
tools?: string[];
tool_kwargs?: Array<unknown>;
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<IAgent, 'versions'>[];
}
import { Schema } from 'mongoose';
import type { IAgent } from '~/types';
const agentSchema = new Schema<IAgent>(
{

View file

@ -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<IAssistant>(
{

View file

@ -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<IMongoFile> = new Schema(
{

View file

@ -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(

View file

@ -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;
};
}

View file

@ -0,0 +1,31 @@
import { Document, Types } from 'mongoose';
export interface IAgent extends Omit<Document, 'model'> {
id: string;
name?: string;
description?: string;
instructions?: string;
avatar?: {
filepath: string;
source: string;
};
provider: string;
model: string;
model_parameters?: Record<string, unknown>;
artifacts?: string;
access_level?: number;
recursion_limit?: number;
tools?: string[];
tool_kwargs?: Array<unknown>;
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<IAgent, 'versions'>[];
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -0,0 +1,27 @@
import { Document, Types } from 'mongoose';
export interface IMongoFile extends Omit<Document, 'model'> {
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;
}

View file

@ -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';

View file

@ -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;
};
};
}