| const fs = require('fs'); |
| const path = require('path'); |
| const mime = require('mime'); |
| const axios = require('axios'); |
| const fetch = require('node-fetch'); |
| const { logger } = require('@librechat/data-schemas'); |
| const { getAzureContainerClient } = require('@librechat/api'); |
|
|
| const defaultBasePath = 'images'; |
| const { AZURE_STORAGE_PUBLIC_ACCESS = 'true', AZURE_CONTAINER_NAME = 'files' } = process.env; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| async function saveBufferToAzure({ |
| userId, |
| buffer, |
| fileName, |
| basePath = defaultBasePath, |
| containerName, |
| }) { |
| try { |
| const containerClient = await getAzureContainerClient(containerName); |
| const access = AZURE_STORAGE_PUBLIC_ACCESS?.toLowerCase() === 'true' ? 'blob' : undefined; |
| |
| await containerClient.createIfNotExists({ access }); |
| const blobPath = `${basePath}/${userId}/${fileName}`; |
| const blockBlobClient = containerClient.getBlockBlobClient(blobPath); |
| await blockBlobClient.uploadData(buffer); |
| return blockBlobClient.url; |
| } catch (error) { |
| logger.error('[saveBufferToAzure] Error uploading buffer:', error); |
| throw error; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| async function saveURLToAzure({ |
| userId, |
| URL, |
| fileName, |
| basePath = defaultBasePath, |
| containerName, |
| }) { |
| try { |
| const response = await fetch(URL); |
| const buffer = await response.buffer(); |
| return await saveBufferToAzure({ userId, buffer, fileName, basePath, containerName }); |
| } catch (error) { |
| logger.error('[saveURLToAzure] Error uploading file from URL:', error); |
| throw error; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| async function getAzureURL({ fileName, basePath = defaultBasePath, userId, containerName }) { |
| try { |
| const containerClient = await getAzureContainerClient(containerName); |
| const blobPath = userId ? `${basePath}/${userId}/${fileName}` : `${basePath}/${fileName}`; |
| const blockBlobClient = containerClient.getBlockBlobClient(blobPath); |
| return blockBlobClient.url; |
| } catch (error) { |
| logger.error('[getAzureURL] Error retrieving blob URL:', error); |
| throw error; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| async function deleteFileFromAzure(req, file) { |
| try { |
| const containerClient = await getAzureContainerClient(AZURE_CONTAINER_NAME); |
| const blobPath = file.filepath.split(`${AZURE_CONTAINER_NAME}/`)[1]; |
| if (!blobPath.includes(req.user.id)) { |
| throw new Error('User ID not found in blob path'); |
| } |
| const blockBlobClient = containerClient.getBlockBlobClient(blobPath); |
| await blockBlobClient.delete(); |
| logger.debug('[deleteFileFromAzure] Blob deleted successfully from Azure Blob Storage'); |
| } catch (error) { |
| logger.error('[deleteFileFromAzure] Error deleting blob:', error); |
| if (error.statusCode === 404) { |
| return; |
| } |
| throw error; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| async function streamFileToAzure({ |
| userId, |
| filePath, |
| fileName, |
| basePath = defaultBasePath, |
| containerName, |
| }) { |
| try { |
| const containerClient = await getAzureContainerClient(containerName); |
| const access = AZURE_STORAGE_PUBLIC_ACCESS?.toLowerCase() === 'true' ? 'blob' : undefined; |
|
|
| |
| await containerClient.createIfNotExists({ access }); |
|
|
| const blobPath = `${basePath}/${userId}/${fileName}`; |
| const blockBlobClient = containerClient.getBlockBlobClient(blobPath); |
|
|
| |
| const stats = await fs.promises.stat(filePath); |
|
|
| |
| const fileStream = fs.createReadStream(filePath); |
|
|
| const blobContentType = mime.getType(fileName); |
| await blockBlobClient.uploadStream( |
| fileStream, |
| undefined, |
| undefined, |
| { |
| blobHTTPHeaders: { |
| blobContentType, |
| }, |
| onProgress: (progress) => { |
| logger.debug( |
| `[streamFileToAzure] Upload progress: ${progress.loadedBytes} bytes of ${stats.size}`, |
| ); |
| }, |
| }, |
| ); |
|
|
| return blockBlobClient.url; |
| } catch (error) { |
| logger.error('[streamFileToAzure] Error streaming file:', error); |
| throw error; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| async function uploadFileToAzure({ |
| req, |
| file, |
| file_id, |
| basePath = defaultBasePath, |
| containerName, |
| }) { |
| try { |
| const inputFilePath = file.path; |
| const stats = await fs.promises.stat(inputFilePath); |
| const bytes = stats.size; |
| const userId = req.user.id; |
| const fileName = `${file_id}__${path.basename(inputFilePath)}`; |
|
|
| const fileURL = await streamFileToAzure({ |
| userId, |
| filePath: inputFilePath, |
| fileName, |
| basePath, |
| containerName, |
| }); |
|
|
| return { filepath: fileURL, bytes }; |
| } catch (error) { |
| logger.error('[uploadFileToAzure] Error uploading file:', error); |
| throw error; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| async function getAzureFileStream(_req, fileURL) { |
| try { |
| const response = await axios({ |
| method: 'get', |
| url: fileURL, |
| responseType: 'stream', |
| }); |
| return response.data; |
| } catch (error) { |
| logger.error('[getAzureFileStream] Error getting blob stream:', error); |
| throw error; |
| } |
| } |
|
|
| module.exports = { |
| saveBufferToAzure, |
| saveURLToAzure, |
| getAzureURL, |
| deleteFileFromAzure, |
| uploadFileToAzure, |
| getAzureFileStream, |
| }; |
|
|