import { IBinaryData , INodeExecutionData } from 'n8n-workflow' ; import { BINARY_ENCODING } from '../Constants' ; import { IBinaryDataConfig , IBinaryDataManager } from '../Interfaces' ; import { BinaryDataFileSystem } from './FileSystem' ; export class BinaryDataManager { private static instance : BinaryDataManager ; private managers : { [ key ] : IBinaryDataManager ; } ; private binaryDataMode : string ; private availableModes : string [ ] ; constructor ( config ) { this . binaryDataMode = config . mode ; this . availableModes = config . availableModes . split ( ',' ) ; this . managers = { } ; } static async init ( config , mainManager = false ) { if ( BinaryDataManager . instance ) { throw new Error ( 'Binary Data Manager already initialized' ) ; } BinaryDataManager . instance = new BinaryDataManager ( config ) ; if ( BinaryDataManager . instance . availableModes . includes ( 'filesystem' ) ) { BinaryDataManager . instance . managers . filesystem = new BinaryDataFileSystem ( config ) ; await BinaryDataManager . instance . managers . filesystem . init ( mainManager ) ; } return undefined ; } static getInstance ( ) { if ( ! BinaryDataManager . instance ) { throw new Error ( 'Binary Data Manager not initialized' ) ; } return BinaryDataManager . instance ; } async storeBinaryData ( binaryData , binaryBuffer , executionId ) { const retBinaryData = binaryData ; if ( this . managers [ this . binaryDataMode ] ) { return this . managers [ this . binaryDataMode ] . storeBinaryData ( binaryBuffer , executionId ) . then ( ( filename ) => { retBinaryData . id = this . generateBinaryId ( filename ) ; return retBinaryData ; } ) ; } retBinaryData . data = binaryBuffer . toString ( BINARY_ENCODING ) ; return binaryData ; } async retrieveBinaryData ( binaryData ) { if ( binaryData . id ) { return this . retrieveBinaryDataByIdentifier ( binaryData . id ) ; } return Buffer . from ( binaryData . data , BINARY_ENCODING ) ; } async retrieveBinaryDataByIdentifier ( identifier ) { const { mode , id } = this . splitBinaryModeFileId ( identifier ) ; if ( this . managers [ mode ] ) { return this . managers [ mode ] . retrieveBinaryDataByIdentifier ( id ) ; } throw new Error ( 'Storage mode used to store binary data not available' ) ; } async markDataForDeletionByExecutionId ( executionId ) { if ( this . managers [ this . binaryDataMode ] ) { return this . managers [ this . binaryDataMode ] . markDataForDeletionByExecutionId ( executionId ) ; } return Promise . resolve ( ) ; } async persistBinaryDataForExecutionId ( executionId ) { if ( this . managers [ this . binaryDataMode ] ) { return this . managers [ this . binaryDataMode ] . persistBinaryDataForExecutionId ( executionId ) ; } return Promise . resolve ( ) ; } async deleteBinaryDataByExecutionId ( executionId ) { if ( this . managers [ this . binaryDataMode ] ) { return this . managers [ this . binaryDataMode ] . deleteBinaryDataByExecutionId ( executionId ) ; } return Promise . resolve ( ) ; } async duplicateBinaryData ( inputData , executionId ) { if ( inputData && this . managers [ this . binaryDataMode ] ) { const returnInputData = ( inputData as INodeExecutionData [ ] [ ] ) . map ( async ( executionDataArray ) => { if ( executionDataArray ) { return Promise . all ( executionDataArray . map ( ( executionData ) => { if ( executionData . binary ) { return this . duplicateBinaryDataInExecData ( executionData , executionId ) ; } return executionData ; } ) ) ; } return executionDataArray ; } ) ; return Promise . all ( returnInputData ) ; } return Promise . resolve ( inputData as INodeExecutionData [ ] [ ] ) ; } private generateBinaryId ( filename ) { return `${ this . binaryDataMode }:${ filename }` ; } private splitBinaryModeFileId ( fileId ) { const [ mode , id ] = fileId . split ( ':' ) ; return { mode , id } ; } private async duplicateBinaryDataInExecData ( executionData , executionId ) { const binaryManager = this . managers [ this . binaryDataMode ] ; if ( executionData . binary ) { const binaryDataKeys = Object . keys ( executionData . binary ) ; const bdPromises = binaryDataKeys . map ( async ( key ) => { if ( ! executionData . binary ) { return { key , newId : undefined } ; } const binaryDataId = executionData . binary [ key ] . id ; if ( ! binaryDataId ) { return { key , newId : undefined } ; } return binaryManager ?. duplicateBinaryDataByIdentifier ( this . splitBinaryModeFileId ( binaryDataId ) . id , executionId ) . then ( ( filename ) => ( { newId : this . generateBinaryId ( filename ) , key , } ) ) ; } ) ; return Promise . all ( bdPromises ) . then ( ( b ) => { return b . reduce ( ( acc , curr ) => { if ( acc . binary && curr ) { acc . binary [ curr . key ] . id = curr . newId ; } return acc ; } , executionData ) ; } ) ; } return executionData ; } }