mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-08-04 06:52:47 +00:00
⚙️ perf: reduce first-load MongoDB round trips (#14101)
* perf(api): reduce first-load database round trips * docs: move agent guidance to claude docs * refactor(api): move message validation into api package * fix(api): narrow active generation job lookup * fix(api): preserve omitted source identity
This commit is contained in:
parent
a0aa1f2b9d
commit
44d1275f36
25 changed files with 895 additions and 242 deletions
|
|
@ -3,9 +3,12 @@ jest.mock('~/models', () => ({
|
|||
}));
|
||||
|
||||
jest.mock('@librechat/api', () => ({
|
||||
createMessageRequestMiddleware:
|
||||
jest.requireActual('@librechat/api').createMessageRequestMiddleware,
|
||||
GenerationJobManager: {
|
||||
getJob: jest.fn(),
|
||||
},
|
||||
isPendingActionStale: jest.fn(() => false),
|
||||
}));
|
||||
|
||||
jest.mock('@librechat/data-schemas', () => ({
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
const { logger } = require('@librechat/data-schemas');
|
||||
const { isEmailDomainAllowed } = require('@librechat/api');
|
||||
const { getAppConfigOptionsFromUser, isEmailDomainAllowed } = require('@librechat/api');
|
||||
const { getAppConfig } = require('~/server/services/Config');
|
||||
|
||||
/**
|
||||
|
|
@ -16,11 +16,7 @@ const { getAppConfig } = require('~/server/services/Config');
|
|||
const checkDomainAllowed = async (req, res, next) => {
|
||||
try {
|
||||
const email = req?.user?.email;
|
||||
const appConfig = await getAppConfig({
|
||||
role: req?.user?.role,
|
||||
userId: req?.user?.id,
|
||||
tenantId: req?.user?.tenantId,
|
||||
});
|
||||
const appConfig = await getAppConfig(getAppConfigOptionsFromUser(req?.user));
|
||||
|
||||
if (email && !isEmailDomainAllowed(email, appConfig?.registration?.allowedDomains)) {
|
||||
logger.error(`[Social Login] [Social Login not allowed] [Email: ${email}]`);
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
const { logger } = require('@librechat/data-schemas');
|
||||
const { getAppConfigOptionsFromUser } = require('@librechat/api');
|
||||
const { getAppConfig } = require('~/server/services/Config');
|
||||
|
||||
const configMiddleware = async (req, res, next) => {
|
||||
try {
|
||||
const userRole = req.user?.role;
|
||||
const userId = req.user?.id;
|
||||
const tenantId = req.user?.tenantId;
|
||||
req.config = await getAppConfig({ role: userRole, userId, tenantId });
|
||||
req.config = await getAppConfig(getAppConfigOptionsFromUser(req.user));
|
||||
|
||||
next();
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ const setTwoFactorTempUser = require('./setTwoFactorTempUser');
|
|||
const validateRegistration = require('./validateRegistration');
|
||||
const buildEndpointOption = require('./buildEndpointOption');
|
||||
const validateMessageReq = require('./validateMessageReq');
|
||||
const { prepareMessageRequestValidation, sendValidationResponse } = require('./messageValidation');
|
||||
const checkDomainAllowed = require('./checkDomainAllowed');
|
||||
const requireLocalAuth = require('./requireLocalAuth');
|
||||
const canDeleteAccount = require('./canDeleteAccount');
|
||||
|
|
@ -47,6 +48,8 @@ module.exports = {
|
|||
configMiddleware,
|
||||
checkDomainAllowed,
|
||||
validateMessageReq,
|
||||
sendValidationResponse,
|
||||
prepareMessageRequestValidation,
|
||||
buildEndpointOption,
|
||||
validateRegistration,
|
||||
validatePasswordReset,
|
||||
|
|
|
|||
14
api/server/middleware/messageValidation.js
Normal file
14
api/server/middleware/messageValidation.js
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
const {
|
||||
GenerationJobManager,
|
||||
createMessageRequestMiddleware,
|
||||
isPendingActionStale,
|
||||
} = require('@librechat/api');
|
||||
const { logger } = require('@librechat/data-schemas');
|
||||
const { getConvo } = require('~/models');
|
||||
|
||||
module.exports = createMessageRequestMiddleware({
|
||||
getConvo,
|
||||
getJob: (conversationId) => GenerationJobManager.getJob(conversationId),
|
||||
isPendingActionStale,
|
||||
logger,
|
||||
});
|
||||
|
|
@ -1,78 +1,3 @@
|
|||
const { GenerationJobManager, isPendingActionStale } = require('@librechat/api');
|
||||
const { logger } = require('@librechat/data-schemas');
|
||||
const { getConvo } = require('~/models');
|
||||
|
||||
function hasTenantMismatch(job, user) {
|
||||
// Untenanted jobs remain readable by their owner for pre-multi-tenancy deployments.
|
||||
return job.metadata?.tenantId != null && job.metadata.tenantId !== user.tenantId;
|
||||
}
|
||||
|
||||
async function canReadActiveJobConversation(req, conversationId) {
|
||||
if (req.method !== 'GET' || req.params?.messageId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let job;
|
||||
try {
|
||||
job = await GenerationJobManager.getJob(conversationId);
|
||||
} catch (error) {
|
||||
logger.warn(`[validateMessageReq] Active job lookup failed for ${conversationId}:`, error);
|
||||
return false;
|
||||
}
|
||||
|
||||
// A job paused for human review is still active (consistent with /chat/status
|
||||
// and /chat/active), so a new-conversation run that pauses before its final
|
||||
// save can still recover the prompt — but only while it has a live,
|
||||
// resolvable prompt (missing/malformed or past-expiry reads as inactive).
|
||||
const isActive =
|
||||
!!job &&
|
||||
(job.status === 'running' ||
|
||||
(job.status === 'requires_action' &&
|
||||
!isPendingActionStale({ pendingAction: job.metadata?.pendingAction })));
|
||||
if (!isActive) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return job.metadata?.userId === req.user.id && !hasTenantMismatch(job, req.user);
|
||||
}
|
||||
|
||||
// Middleware to validate conversationId and user relationship
|
||||
const validateMessageReq = async (req, res, next) => {
|
||||
const body = req.body ?? {};
|
||||
const paramConversationId = req.params?.conversationId;
|
||||
const bodyConversationId = body.conversationId;
|
||||
const nestedConversationId = body.message?.conversationId;
|
||||
|
||||
if (
|
||||
(paramConversationId &&
|
||||
((bodyConversationId && paramConversationId !== bodyConversationId) ||
|
||||
(nestedConversationId && paramConversationId !== nestedConversationId))) ||
|
||||
(bodyConversationId && nestedConversationId && bodyConversationId !== nestedConversationId)
|
||||
) {
|
||||
return res.status(400).json({ error: 'Conversation ID mismatch' });
|
||||
}
|
||||
|
||||
const conversationId = paramConversationId || bodyConversationId || nestedConversationId;
|
||||
|
||||
if (conversationId === 'new') {
|
||||
return res.status(200).send([]);
|
||||
}
|
||||
|
||||
const conversation = await getConvo(req.user.id, conversationId);
|
||||
|
||||
if (!conversation) {
|
||||
if (await canReadActiveJobConversation(req, conversationId)) {
|
||||
return next();
|
||||
}
|
||||
|
||||
return res.status(404).json({ error: 'Conversation not found' });
|
||||
}
|
||||
|
||||
if (conversation.user !== req.user.id) {
|
||||
return res.status(403).json({ error: 'User not authorized for this conversation' });
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
const { validateMessageReq } = require('./messageValidation');
|
||||
|
||||
module.exports = validateMessageReq;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue