'use strict' ; import { ListItemObject , ListItemPrefix , ListResults , PutObjectOptions , StorageProvider } from '@smcloudstore/core/dist/StorageProvider' ; import { Stream } from 'stream' ; import B2Upload = require ( './B2Upload' ) ; const B2 = require ( 'backblaze-b2' ) as any ; /**
 * Connection options for a Backblaze B2 provider.
 */ interface BackblazeB2ConnectionOptions { /** Account Id */ accountId : string ; /** Application key (secret key) */ applicationKey : string ; } /**
 * Options passed when creating a container
 */ interface BackblazeB2CreateContainerOptions { /** Determine access level for all files in the container. Defaults to 'private' if not specified */ access ? : 'public' | 'private' ; } /** Dictionary of options used when putting an object. Many providers will extend this. */ interface BackblazeB2PutObjectOptions extends PutObjectOptions { /** When passing a stream as `data` object, being able to specify the length of the data allows for faster uploads; this argument is ignored if `data` is not a Stream object */ length ? : number ; } /**
 * Client to interact with Backblaze B2 cloud storage.
 */ class BackblazeB2Provider extends StorageProvider { /** Specifies for how long (in ms) to keep BucketId data in cache. Set to 0 to disable caching. Default is 15 minutes. */ static bucketIdCacheDuration = 900000 ; protected _client : any ; private _isAuthorized : boolean ; private _bucketIdCache : { [ s ] : { result : string ; time : number ; } ; } ; param connection constructor ( connection ) { if ( ! connection || ! Object . keys ( connection ) . length ) { throw new Error ( 'Connection argument is empty' ) ; } super ( connection ) ; this . _isAuthorized = false ; this . _provider = 'backblaze-b2' ; this . _bucketIdCache = { } ; this . _client = new B2 ( connection ) ; } param container param options returns async createContainer ( container , options ? ) { const access = ( options && options . access && options . access == 'public' ) ? 'allPublic' : 'allPrivate' ; return this . _ensureAuthorized ( ) . then ( ( ) => this . _client . createBucket ( container , access ) ) ; } param container returns async isContainer ( container ) { return this . listContainers ( ) . then ( ( list ) => { return list . indexOf ( container ) >= 0 ; } ) ; } param container param options returns async ensureContainer ( container , options ? ) { return this . isContainer ( container ) . then ( ( exists ) => { if ( ! exists ) { return this . createContainer ( container ) ; } } ) ; } returns async listContainers ( ) { return this . _ensureAuthorized ( ) . then ( ( ) => this . _client . listBuckets ( ) ) . then ( ( response ) => { if ( ! response || ! response . data || ! response . data . buckets || ! Array . isArray ( response . data . buckets ) ) { return [ ] ; } return response . data . buckets . map ( ( el ) => ( el && el . bucketName ) || undefined ) ; } ) ; } param container returns async deleteContainer ( container ) { return Promise . resolve ( ) . then ( ( ) => this . _ensureAuthorized ( ) ) . then ( ( ) => this . _getBucketId ( container ) ) . then ( ( bucketId ) => { if ( ! bucketId ) { throw Error ( 'Container not found: ' + container ) ; } return this . _client . deleteBucket ( bucketId ) ; } ) . then ( ( ) => { return ; } ) ; } param container param path param data param options returns async putObject ( container , path , data , options ? ) { if ( ! options ) { options = { } ; } return Promise . resolve ( ) . then ( ( ) => this . _ensureAuthorized ( ) ) . then ( ( ) => this . _getBucketId ( container ) ) . then ( ( bucketId ) => { const uploader = new B2Upload ( this . _client , bucketId , path , data , options . metadata , options . length ) ; return uploader . start ( ) ; } ) ; } param container param path returns async getObject ( container , path ) { return this . _ensureAuthorized ( ) . then ( ( ) => { return this . _client . downloadFileByName ( { bucketName : container , fileName : path , responseType : 'stream' } ) . then ( ( response ) => { if ( ! response || ! response . data ) { throw Error ( 'Invalid response when requesting the object' ) ; } return response . data ; } ) ; } ) ; } param container param prefix returns async listObjects ( container , prefix ? ) { const list = [ ] as ListResults ; const requestList = ( bucketId , startFileName ) : Promise < ListResults > => { return this . _client . listFileNames ( { bucketId : bucketId , delimiter : '/' , maxFileCount : 1000 , prefix : prefix || '' , startFileName : startFileName } ) . then ( ( response ) => { if ( ! response || ! response . data || ! response . data . files ) { throw Error ( 'Invalid response when listing the container' ) ; } for ( const file of response . data . files ) { if ( file && file . action == 'upload' ) { list . push ( { contentType : file . contentType , lastModified : new Date ( file . uploadTimestamp ) , path : file . fileName , size : file . contentLength } as ListItemObject ) ; } else if ( file && file . action == 'folder' ) { list . push ( { prefix : file . fileName } as ListItemPrefix ) ; } } if ( response . data . nextFileName ) { return requestList ( bucketId , response . data . nextFileName ) ; } else { return list ; } } ) ; } ; return Promise . resolve ( ) . then ( ( ) => this . _ensureAuthorized ( ) ) . then ( ( ) => this . _getBucketId ( container ) ) . then ( ( bucketId ) => { if ( ! bucketId ) { throw Error ( 'Container not found: ' + container ) ; } return requestList ( bucketId , null ) ; } ) ; } param container param path returns async deleteObject ( container , path ) { return Promise . resolve ( ) . then ( ( ) => this . _ensureAuthorized ( ) ) . then ( ( ) => this . _getBucketId ( container ) ) . then ( ( bucketId ) => { if ( ! bucketId ) { throw Error ( 'Container not found: ' + container ) ; } return this . _getFileId ( bucketId , path ) ; } ) . then ( ( fileId ) => { if ( ! fileId ) { throw Error ( 'File not found: ' + container ) ; } return this . _client . deleteFileVersion ( { fileId : fileId , fileName : path } ) ; } ) ; } param container param path param ttl returns async presignedGetUrl ( container , path , ttl ? ) { throw Error ( 'Presigned URLs are not supported by the BackblazeB2 Provider' ) ; } param container param path param options param ttl returns async presignedPutUrl ( container , path , options ? , ttl ? ) { throw Error ( 'Presigned URLs are not supported by the BackblazeB2 Provider' ) ; } param bucketName returns async private _getBucketId ( bucketName ) { const cachingEnabled = BackblazeB2Provider . bucketIdCacheDuration && BackblazeB2Provider . bucketIdCacheDuration > 0 ; if ( cachingEnabled && this . _bucketIdCache [ bucketName ] && ( Date . now ( ) - this . _bucketIdCache [ bucketName ] . time < BackblazeB2Provider . bucketIdCacheDuration ) ) { return Promise . resolve ( this . _bucketIdCache [ bucketName ] . result ) ; } return Promise . resolve ( ) . then ( ( ) => this . _client . listBuckets ( ) ) . then ( ( response ) => { if ( ! response || ! response . data || ! response . data . buckets || ! Array . isArray ( response . data . buckets ) ) { return null ; } for ( const el of response . data . buckets ) { if ( el . bucketName == bucketName ) { if ( cachingEnabled ) { this . _bucketIdCache [ bucketName ] = { result : el . bucketId , time : Date . now ( ) } ; const keys = Object . keys ( this . _bucketIdCache ) ; if ( keys . length > 100 ) { delete this . _bucketIdCache [ keys . shift ( ) ] ; } } return el . bucketId as string ; } } return null ; } ) ; } param bucketId param fileName returns async private _getFileId ( bucketId , fileName ) { return this . _client . listFileNames ( { bucketId : bucketId , delimiter : '/' , maxFileCount : 1000 , prefix : fileName } ) . then ( ( response ) => { if ( ! response || ! response . data || ! response . data . files || ! Array . isArray ( response . data . files ) ) { return null ; } for ( const file of response . data . files ) { if ( file && file . fileName == fileName ) { return file . fileId ; } } return null ; } ) ; } returns async private _ensureAuthorized ( ) { if ( this . _isAuthorized ) { return Promise . resolve ( ) ; } else { return this . _client . authorize ( ) ; } } } export = BackblazeB2Provider ;