import { Storage as DStorage } from '../../Plugin/Storage' ; import { Storage , Bucket , GetSignedUrlConfig } from '@google-cloud/storage' ; import { DeepPartial } from '../../utils/DeepPartial' ; import { Dimension , StorageType } from '../../Dimension' ; import { Plugin } from '../../Plugin' ; import path from 'path' ; import fs , { copyFileSync } from 'fs' ; import { Database } from '../../Plugin/Database' ; import { Tournament } from '../../Tournament' ; import LRUFileCache from '../../utils/LRUFileCache' ; import { LOCAL_DIR } from '../../utils/System' ; import { Logger } from '../../Logger' ; import { deepCopy } from '../../utils/DeepCopy' ; import { deepMerge } from '../../utils/DeepMerge' ; export class GCloudStorage extends DStorage { public name = 'GCloudStorage' ; public type : Plugin . Type = Plugin . Type . STORAGE ; public storage : Storage ; public configs : GCloudStorage . Configs = { keyFilename : '' , projectId : '' , fileCacheMaxSize : 1024 * 1024 * 1024 , loggingLevel : Logger . LEVEL . INFO , cacheDir : 'cache' , } ; public log : Logger ; public dimensionBucket : Bucket ; private lruFileCache : LRUFileCache ; constructor ( configs ) { super ( ) ; this . configs = deepMerge ( this . configs , deepCopy ( configs ) ) ; this . lruFileCache = new LRUFileCache ( this . configs . fileCacheMaxSize , path . join ( LOCAL_DIR , this . configs . cacheDir ) ) ; this . log = new Logger ( this . configs . loggingLevel , 'GCloud-Storage' ) ; } /**
     * Initializer. Initializes the storage object and creates necessary buckets
     */ async initialize ( dimension ) { const bucketName = dimension . name . toLowerCase ( ) . replace ( / /g , '_' ) + '_' + dimension . id . toLowerCase ( ) ; this . storage = new Storage ( { keyFilename : this . configs . keyFilename , projectId : this . configs . projectId , } ) ; const exists = await this . storage . bucket ( bucketName ) . exists ( ) ; if ( ! exists [ 0 ] ) { this . log . system ( `creating bucket ${ bucketName }` ) ; await this . storage . createBucket ( bucketName ) ; } this . dimensionBucket = this . storage . bucket ( bucketName ) ; } async uploadTournamentFile ( file , user , tournament ) { const dest = `users/${ user . username }_${ user . playerID }/tournaments/${ tournament . getKeyName ( ) }/bot.zip` ; return this . dimensionBucket . upload ( file , { destination : dest , } ) . then ( ( ) => { this . log . system ( `uploaded tournament file for player ${ user . playerID } to ${ dest }` ) ; return dest ; } ) ; } async upload ( file , destination ? ) { const dest = `${ destination ? destination : path . basename ( file ) }` ; return this . dimensionBucket . upload ( file , { destination : dest , } ) . then ( ( ) => { this . log . system ( `uploaded file from ${ file } to ${ dest }` ) ; return dest ; } ) ; } async download ( key , destination , useCached ) { return new Promise ( ( resolve ) => { if ( useCached ) { const cachedPath = this . lruFileCache . get ( key ) ; if ( cachedPath ) { copyFileSync ( cachedPath , destination ) ; resolve ( destination ) ; return ; } } const file = this . dimensionBucket . file ( key ) ; const ws = file . createReadStream ( ) . pipe ( fs . createWriteStream ( destination ) ) ; ws . on ( 'close' , async ( ) => { const cachedPath = await this . lruFileCache . add ( key , destination ) ; this . log . system ( `writing from bucket ${ key } -> ${ destination }; cached to ${ cachedPath }` ) ; resolve ( destination ) ; } ) ; } ) ; } param key async getDownloadURL ( key ) { const options = { version : 'v4' , action : 'read' , expires : new Date ( ) . getTime ( ) + 15 * 60 * 1000 , } ; return this . dimensionBucket . file ( key ) . getSignedUrl ( options ) . then ( ( url ) => { return url [ 0 ] ; } ) ; } public async manipulate ( dimension ) { dimension . configs . backingDatabase = StorageType . GCLOUD ; return ; } } export namespace GCloudStorage { /**
     * Specific configurations for GCloud storage
     */ export interface Configs extends DeepPartial < DStorage . Configs > { /**
         * Path to key file from a google account service key
         */ keyFilename : string ; /**
         * Project ID
         */ projectId : string ; default fileCacheMaxSize : number ; default cacheDir : string ; /**
         * Logging level for storage service
         */ loggingLevel : Logger . LEVEL ; } }