import { IHealthCheckResult , IStorages , IStoragesModule , } from "@staticdeploy/core" ; import { S3 } from "aws-sdk" ; import { Knex , knex } from "knex" ; import { extname , join } from "path" ; import AppsStorage from "./AppsStorage" ; import BundlesStorage from "./BundlesStorage" ; import { StorageSetupError } from "./common/errors" ; import EntrypointsStorage from "./EntrypointsStorage" ; import GroupsStorage from "./GroupsStorage" ; import OperationLogsStorage from "./OperationLogsStorage" ; import UsersStorage from "./UsersStorage" ; export default class PgS3Storages implements IStoragesModule { private knex : Knex ; private s3Client : S3 ; private s3Bucket : string ; private s3EnableGCSCompatibility : boolean ; constructor ( options ) { this . knex = knex ( options . postgresUrl ) ; this . s3Bucket = options . s3Config . bucket ; this . s3Client = new S3 ( { endpoint : options . s3Config . endpoint , accessKeyId : options . s3Config . accessKeyId , secretAccessKey : options . s3Config . secretAccessKey , s3ForcePathStyle : true , } ) ; this . s3EnableGCSCompatibility = options . s3Config . enableGCSCompatibility ; } async setup ( ) { await this . runSqlMigrations ( ) ; await this . createS3Bucket ( ) ; } getStorages ( ) { return { apps : new AppsStorage ( this . knex ) , bundles : new BundlesStorage ( this . knex , this . s3Client , this . s3Bucket , this . s3EnableGCSCompatibility ) , entrypoints : new EntrypointsStorage ( this . knex ) , groups : new GroupsStorage ( this . knex ) , operationLogs : new OperationLogsStorage ( this . knex ) , users : new UsersStorage ( this . knex ) , checkHealth : this . checkHealth . bind ( this ) , } ; } private async checkHealth ( ) { const healthCheckResult = { isHealthy : true , details : { } , } ; try { await this . knex . raw ( "select 1" ) ; } catch ( err ) { healthCheckResult . isHealthy = false ; healthCheckResult . details . postgres = { message : "Unable to run query 'select 1'" , err : err , } ; } try { await this . s3Client . headBucket ( { Bucket : this . s3Bucket } ) . promise ( ) ; } catch ( err ) { healthCheckResult . isHealthy = false ; healthCheckResult . isHealthy = false ; healthCheckResult . details . s3 = { message : `Unable to HEAD bucket ${ this . s3Bucket }` , err : err , } ; } return healthCheckResult ; } private async runSqlMigrations ( ) { const isCurrentFileTs = extname ( __filename ) === ".ts" ; try { await this . knex . migrate . latest ( { directory : join ( __dirname , "./migrations" ) , loadExtensions : [ isCurrentFileTs ? ".ts" : ".js" ] , } as any ) ; } catch ( err ) { throw new StorageSetupError ( "Error running sql migration" , err ) ; } } private async createS3Bucket ( ) { try { await this . s3Client . headBucket ( { Bucket : this . s3Bucket } ) . promise ( ) ; return ; } catch ( err ) { if ( err . statusCode !== 404 ) { throw new StorageSetupError ( `Error accessing bucket = ${ this . s3Bucket }` , err ) ; } } try { await this . s3Client . createBucket ( { Bucket : this . s3Bucket } ) . promise ( ) ; } catch ( err ) { throw new StorageSetupError ( `Error creating bucket = ${ this . s3Bucket }` , err ) ; } } }