LibreChat/api/server/routes/files/multer.js
Danny Avila 2524d33362
📂 refactor: Cleanup File Filtering Logic, Improve Validation (#10414)
* feat: add filterFilesByEndpointConfig to filter disabled file processing by provider

* chore: explicit define of endpointFileConfig for better debugging

* refactor: move `normalizeEndpointName` to data-provider as used app-wide

* chore: remove overrideEndpoint from useFileHandling

* refactor: improve endpoint file config selection

* refactor: update filterFilesByEndpointConfig to accept structured parameters and improve endpoint file config handling

* refactor: replace defaultFileConfig with getEndpointFileConfig for improved file configuration handling across components

* test: add comprehensive unit tests for getEndpointFileConfig to validate endpoint configuration handling

* refactor: streamline agent endpoint assignment and improve file filtering logic

* feat: add error handling for disabled file uploads in endpoint configuration

* refactor: update encodeAndFormat functions to accept structured parameters for provider and endpoint

* refactor: streamline requestFiles handling in initializeAgent function

* fix: getEndpointFileConfig partial config merging scenarios

* refactor: enhance mergeWithDefault function to support document-supported providers with comprehensive MIME types

* refactor: user-configured default file config in getEndpointFileConfig

* fix: prevent file handling when endpoint is disabled and file is dragged to chat

* refactor: move `getEndpointField` to `data-provider` and update usage across components and hooks

* fix: prioritize endpointType based on agent.endpoint in file filtering logic

* fix: prioritize agent.endpoint in file filtering logic and remove unnecessary endpointType defaulting
2025-11-10 19:05:30 -05:00

88 lines
2.5 KiB
JavaScript

const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const multer = require('multer');
const { sanitizeFilename } = require('@librechat/api');
const {
mergeFileConfig,
getEndpointFileConfig,
fileConfig: defaultFileConfig,
} = require('librechat-data-provider');
const { getAppConfig } = require('~/server/services/Config');
const storage = multer.diskStorage({
destination: function (req, file, cb) {
const appConfig = req.config;
const outputPath = path.join(appConfig.paths.uploads, 'temp', req.user.id);
if (!fs.existsSync(outputPath)) {
fs.mkdirSync(outputPath, { recursive: true });
}
cb(null, outputPath);
},
filename: function (req, file, cb) {
req.file_id = crypto.randomUUID();
file.originalname = decodeURIComponent(file.originalname);
const sanitizedFilename = sanitizeFilename(file.originalname);
cb(null, sanitizedFilename);
},
});
const importFileFilter = (req, file, cb) => {
if (file.mimetype === 'application/json') {
cb(null, true);
} else if (path.extname(file.originalname).toLowerCase() === '.json') {
cb(null, true);
} else {
cb(new Error('Only JSON files are allowed'), false);
}
};
/**
*
* @param {import('librechat-data-provider').FileConfig | undefined} customFileConfig
*/
const createFileFilter = (customFileConfig) => {
/**
* @param {ServerRequest} req
* @param {Express.Multer.File}
* @param {import('multer').FileFilterCallback} cb
*/
const fileFilter = (req, file, cb) => {
if (!file) {
return cb(new Error('No file provided'), false);
}
if (req.originalUrl.endsWith('/speech/stt') && file.mimetype.startsWith('audio/')) {
return cb(null, true);
}
const endpoint = req.body.endpoint;
const endpointType = req.body.endpointType;
const endpointFileConfig = getEndpointFileConfig({
fileConfig: customFileConfig,
endpoint,
endpointType,
});
if (!defaultFileConfig.checkType(file.mimetype, endpointFileConfig.supportedMimeTypes)) {
return cb(new Error('Unsupported file type: ' + file.mimetype), false);
}
cb(null, true);
};
return fileFilter;
};
const createMulterInstance = async () => {
const appConfig = await getAppConfig();
const fileConfig = mergeFileConfig(appConfig?.fileConfig);
const fileFilter = createFileFilter(fileConfig);
return multer({
storage,
fileFilter,
limits: { fileSize: fileConfig.serverFileSizeLimit },
});
};
module.exports = { createMulterInstance, storage, importFileFilter };