mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-17 00:40:14 +01:00
📦 chore: Bump Express.js to v5 (#10671)
* chore: update express to version 5.1.0 in package.json * chore: update express-rate-limit to version 8.2.1 in package.json and package-lock.json * fix: Enhance server startup error handling in experimental and index files * Added error handling for server startup in both experimental.js and index.js to log errors and exit the process if the server fails to start. * Updated comments in openidStrategy.js to clarify the purpose of the CustomOpenIDStrategy class and its relation to Express version changes. * chore: Implement rate limiting for all POST routes excluding /speech, required for express v5 * Added middleware to apply IP and user rate limiters to all POST requests, ensuring that the /speech route remains unaffected. * Enhanced code clarity with comments explaining the new rate limiting logic. * chore: Enable writable req.query for mongoSanitize compatibility in Express 5 * chore: Ensure req.body exists in multiple middleware and route files for Express 5 compatibility
This commit is contained in:
parent
3c54740074
commit
19b78ecd81
11 changed files with 433 additions and 422 deletions
|
|
@ -58,9 +58,9 @@
|
|||
"dedent": "^1.5.3",
|
||||
"dotenv": "^16.0.3",
|
||||
"eventsource": "^3.0.2",
|
||||
"express": "^4.21.2",
|
||||
"express": "^5.1.0",
|
||||
"express-mongo-sanitize": "^2.2.0",
|
||||
"express-rate-limit": "^7.4.1",
|
||||
"express-rate-limit": "^8.2.1",
|
||||
"express-session": "^1.18.2",
|
||||
"express-static-gzip": "^2.2.0",
|
||||
"file-type": "^18.7.0",
|
||||
|
|
|
|||
|
|
@ -138,6 +138,7 @@ const listAssistantsForAzure = async ({ req, res, version, azureConfig = {}, que
|
|||
|
||||
/* The specified model is only necessary to
|
||||
fetch assistants for the shared instance */
|
||||
req.body = req.body || {}; // Express 5: req.body is undefined instead of {} when no body parser runs
|
||||
req.body.model = currentModelTuples[0][0];
|
||||
promises.push(listAllAssistants({ req, res, version, query }));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -246,7 +246,22 @@ if (cluster.isMaster) {
|
|||
app.use(noIndex);
|
||||
app.use(express.json({ limit: '3mb' }));
|
||||
app.use(express.urlencoded({ extended: true, limit: '3mb' }));
|
||||
|
||||
app.use(handleJsonParseError);
|
||||
|
||||
/**
|
||||
* Express 5 Compatibility: Make req.query writable for mongoSanitize
|
||||
* In Express 5, req.query is read-only by default, but express-mongo-sanitize needs to modify it
|
||||
*/
|
||||
app.use((req, _res, next) => {
|
||||
Object.defineProperty(req, 'query', {
|
||||
...Object.getOwnPropertyDescriptor(req, 'query'),
|
||||
value: req.query,
|
||||
writable: true,
|
||||
});
|
||||
next();
|
||||
});
|
||||
|
||||
app.use(mongoSanitize());
|
||||
app.use(cors());
|
||||
app.use(cookieParser());
|
||||
|
|
@ -328,7 +343,12 @@ if (cluster.isMaster) {
|
|||
});
|
||||
|
||||
/** Start listening on shared port (cluster will distribute connections) */
|
||||
app.listen(port, host, async () => {
|
||||
app.listen(port, host, async (err) => {
|
||||
if (err) {
|
||||
logger.error(`Worker ${process.pid} failed to start server:`, err);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`Worker ${process.pid} started: Server listening at http://${
|
||||
host == '0.0.0.0' ? 'localhost' : host
|
||||
|
|
|
|||
|
|
@ -83,6 +83,20 @@ const startServer = async () => {
|
|||
app.use(express.json({ limit: '3mb' }));
|
||||
app.use(express.urlencoded({ extended: true, limit: '3mb' }));
|
||||
app.use(handleJsonParseError);
|
||||
|
||||
/**
|
||||
* Express 5 Compatibility: Make req.query writable for mongoSanitize
|
||||
* In Express 5, req.query is read-only by default, but express-mongo-sanitize needs to modify it
|
||||
*/
|
||||
app.use((req, _res, next) => {
|
||||
Object.defineProperty(req, 'query', {
|
||||
...Object.getOwnPropertyDescriptor(req, 'query'),
|
||||
value: req.query,
|
||||
writable: true,
|
||||
});
|
||||
next();
|
||||
});
|
||||
|
||||
app.use(mongoSanitize());
|
||||
app.use(cors());
|
||||
app.use(cookieParser());
|
||||
|
|
@ -161,7 +175,12 @@ const startServer = async () => {
|
|||
res.send(updatedIndexHtml);
|
||||
});
|
||||
|
||||
app.listen(port, host, async () => {
|
||||
app.listen(port, host, async (err) => {
|
||||
if (err) {
|
||||
logger.error('Failed to start server:', err);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (host === '0.0.0.0') {
|
||||
logger.info(
|
||||
`Server listening on all interfaces at port ${port}. Use http://localhost:${port} to access it`,
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ async function abortRun(req, res) {
|
|||
const conversation = await getConvo(req.user.id, conversationId);
|
||||
|
||||
if (conversation?.model) {
|
||||
req.body = req.body || {}; // Express 5: ensure req.body exists
|
||||
req.body.model = conversation.model;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ async function buildEndpointOption(req, res, next) {
|
|||
: buildFunction[endpointType ?? endpoint];
|
||||
|
||||
// TODO: use object params
|
||||
req.body = req.body || {}; // Express 5: ensure req.body exists
|
||||
req.body.endpointOption = await builder(endpoint, parsedBody, endpointType);
|
||||
|
||||
if (req.body.files && !isAgents) {
|
||||
|
|
|
|||
|
|
@ -154,6 +154,7 @@ router.post('/:assistant_id', async (req, res) => {
|
|||
router.delete('/:assistant_id/:action_id/:model', async (req, res) => {
|
||||
try {
|
||||
const { assistant_id, action_id, model } = req.params;
|
||||
req.body = req.body || {}; // Express 5: ensure req.body exists
|
||||
req.body.model = model;
|
||||
const { openai } = await getOpenAIClient({ req, res });
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,20 @@ const initialize = async () => {
|
|||
router.use('/speech', speech);
|
||||
|
||||
const { fileUploadIpLimiter, fileUploadUserLimiter } = createFileLimiters();
|
||||
router.post('*', fileUploadIpLimiter, fileUploadUserLimiter);
|
||||
|
||||
/** Apply rate limiters to all POST routes (excluding /speech which is handled above) */
|
||||
router.use((req, res, next) => {
|
||||
if (req.method === 'POST' && !req.path.startsWith('/speech')) {
|
||||
return fileUploadIpLimiter(req, res, (err) => {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
return fileUploadUserLimiter(req, res, next);
|
||||
});
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
router.post('/', upload.single('file'));
|
||||
router.post('/images', upload.single('file'));
|
||||
router.post('/images/avatar', upload.single('file'));
|
||||
|
|
|
|||
|
|
@ -99,9 +99,14 @@ This violates RFC 7235 and may cause issues with strict OAuth clients. Removing
|
|||
/** @typedef {Configuration | null} */
|
||||
let openidConfig = null;
|
||||
|
||||
//overload currenturl function because of express version 4 buggy req.host doesn't include port
|
||||
//More info https://github.com/panva/openid-client/pull/713
|
||||
|
||||
/**
|
||||
* Custom OpenID Strategy
|
||||
*
|
||||
* Note: Originally overrode currentUrl() to work around Express 4's req.host not including port.
|
||||
* With Express 5, req.host now includes the port by default, but we continue to use DOMAIN_SERVER
|
||||
* for consistency and explicit configuration control.
|
||||
* More info: https://github.com/panva/openid-client/pull/713
|
||||
*/
|
||||
class CustomOpenIDStrategy extends OpenIDStrategy {
|
||||
currentUrl(req) {
|
||||
const hostAndProtocol = process.env.DOMAIN_SERVER;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue