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