@@ -9,25 +9,29 @@ import { Injectable } from '@nestjs/common';
99import crypto from 'crypto' ;
1010import * as Minio from 'minio' ;
1111
12- import { isNotFoundError } from '../../common/errors/minio' ;
13- import { UploadedFile } from '../../common/interfaces/s3' ;
1412import { FortuneFinalResult } from '../../common/interfaces/job-result' ;
1513import { PGPConfigService } from '../../config/pgp-config.service' ;
1614import { S3ConfigService } from '../../config/s3-config.service' ;
1715import logger from '../../logger' ;
1816import * as httpUtils from '../../utils/http' ;
1917
2018import { Web3Service } from '../web3/web3.service' ;
19+ import { MinioErrorCodes } from './minio.constants' ;
20+
21+ type UploadedFile = {
22+ url : string ;
23+ hash : string ;
24+ } ;
2125
2226@Injectable ( )
2327export class StorageService {
2428 private readonly logger = logger . child ( { context : StorageService . name } ) ;
2529
26- readonly minioClient : Minio . Client ;
30+ private readonly minioClient : Minio . Client ;
2731
2832 constructor (
29- public readonly s3ConfigService : S3ConfigService ,
30- public readonly pgpConfigService : PGPConfigService ,
33+ private readonly s3ConfigService : S3ConfigService ,
34+ private readonly pgpConfigService : PGPConfigService ,
3135 private readonly web3Service : Web3Service ,
3236 ) {
3337 this . minioClient = new Minio . Client ( {
@@ -39,16 +43,32 @@ export class StorageService {
3943 } ) ;
4044 }
4145
42- getUrl ( key : string ) : string {
46+ private getUrl ( key : string ) : string {
4347 return `${ this . s3ConfigService . useSSL ? 'https' : 'http' } ://${
4448 this . s3ConfigService . endpoint
4549 } :${ this . s3ConfigService . port } /${ this . s3ConfigService . bucket } /${ key } `;
4650 }
4751
52+ private async checkFileExists ( key : string ) : Promise < boolean > {
53+ try {
54+ await this . minioClient . statObject ( this . s3ConfigService . bucket , key ) ;
55+ return true ;
56+ } catch ( error ) {
57+ if ( error ?. code === MinioErrorCodes . NotFound ) {
58+ return false ;
59+ }
60+ this . logger . error ( 'Failed to check if file exists' , {
61+ fileKey : key ,
62+ error,
63+ } ) ;
64+ throw new Error ( 'Error accessing storage' ) ;
65+ }
66+ }
67+
4868 private async encryptFile (
4969 escrowAddress : string ,
5070 chainId : ChainId ,
51- content : any ,
71+ content : string | Buffer ,
5272 ) {
5373 if ( ! this . pgpConfigService . encrypt ) {
5474 return content ;
@@ -60,13 +80,11 @@ export class StorageService {
6080 const jobLauncherAddress =
6181 await escrowClient . getJobLauncherAddress ( escrowAddress ) ;
6282
63- const reputationOraclePublicKey = await KVStoreUtils . getPublicKey (
64- chainId ,
65- signer . address ,
66- ) ;
67- const jobLauncherPublicKey = await KVStoreUtils . getPublicKey (
68- chainId ,
69- jobLauncherAddress ,
83+ const [ reputationOraclePublicKey , jobLauncherPublicKey ] = await Promise . all (
84+ [
85+ KVStoreUtils . getPublicKey ( chainId , signer . address ) ,
86+ KVStoreUtils . getPublicKey ( chainId , jobLauncherAddress ) ,
87+ ] ,
7088 ) ;
7189
7290 if ( ! reputationOraclePublicKey || ! jobLauncherPublicKey ) {
@@ -86,8 +104,8 @@ export class StorageService {
86104 }
87105
88106 const encryption = await Encryption . build (
89- this . pgpConfigService . privateKey ! ,
90- this . pgpConfigService . passphrase ,
107+ this . pgpConfigService . privateKey as string ,
108+ this . pgpConfigService . passphrase as string ,
91109 ) ;
92110
93111 const decryptedData = await encryption . decrypt ( contentAsString ) ;
@@ -104,7 +122,7 @@ export class StorageService {
104122 let jsonLikeData = fileContent . toString ( ) ;
105123 try {
106124 jsonLikeData = JSON . parse ( jsonLikeData ) ;
107- } catch ( _noop ) { }
125+ } catch ( noop ) { }
108126
109127 return jsonLikeData ;
110128 } catch ( error ) {
@@ -121,8 +139,12 @@ export class StorageService {
121139 chainId : ChainId ,
122140 solutions : FortuneFinalResult [ ] ,
123141 ) : Promise < UploadedFile > {
124- if ( ! ( await this . minioClient . bucketExists ( this . s3ConfigService . bucket ) ) ) {
125- throw new Error ( 'Bucket not found' ) ;
142+ const isConfiguredBucketExists = await this . minioClient . bucketExists (
143+ this . s3ConfigService . bucket ,
144+ ) ;
145+
146+ if ( ! isConfiguredBucketExists ) {
147+ throw new Error ( "Can't find configured bucket" ) ;
126148 }
127149
128150 try {
@@ -134,22 +156,11 @@ export class StorageService {
134156
135157 const hash = crypto . createHash ( 'sha1' ) . update ( content ) . digest ( 'hex' ) ;
136158 const key = `${ hash } .json` ;
159+ const url = this . getUrl ( key ) ;
137160
138- // Check if the file already exists in the bucket
139- try {
140- await this . minioClient . statObject ( this . s3ConfigService . bucket , key ) ;
141- this . logger . info ( 'File already exist. Skipping upload' , {
142- fileKey : key ,
143- } ) ;
144- return { url : this . getUrl ( key ) , hash } ;
145- } catch ( error ) {
146- if ( ! isNotFoundError ( error ) ) {
147- this . logger . error ( 'Error checking if file exists' , {
148- error,
149- fileKey : key ,
150- } ) ;
151- throw new Error ( 'Error accessing storage' ) ;
152- }
161+ const isAlreadyUploaded = await this . checkFileExists ( key ) ;
162+ if ( isAlreadyUploaded ) {
163+ return { url, hash } ;
153164 }
154165
155166 await this . minioClient . putObject (
@@ -162,8 +173,13 @@ export class StorageService {
162173 } ,
163174 ) ;
164175
165- return { url : this . getUrl ( key ) , hash } ;
166- } catch ( noop ) {
176+ return { url, hash } ;
177+ } catch ( error ) {
178+ this . logger . error ( 'Failed to upload job solutions' , {
179+ error,
180+ escrowAddress,
181+ chainId,
182+ } ) ;
167183 throw new Error ( 'File not uploaded' ) ;
168184 }
169185 }
@@ -177,10 +193,10 @@ export class StorageService {
177193 async copyFileFromURLToBucket (
178194 escrowAddress : string ,
179195 chainId : ChainId ,
180- url : string ,
196+ originalFileUrl : string ,
181197 ) : Promise < UploadedFile > {
182198 try {
183- let fileContent = await httpUtils . downloadFile ( url ) ;
199+ let fileContent = await httpUtils . downloadFile ( originalFileUrl ) ;
184200 fileContent = await this . maybeDecryptFile ( fileContent ) ;
185201 // Encrypt for job launcher
186202 const content = await this . encryptFile (
@@ -189,25 +205,13 @@ export class StorageService {
189205 fileContent ,
190206 ) ;
191207
192- // Upload the encrypted file to the bucket
193208 const hash = crypto . createHash ( 'sha1' ) . update ( content ) . digest ( 'hex' ) ;
194209 const key = `s3${ hash } .zip` ;
210+ const copiedFileurl = this . getUrl ( key ) ;
195211
196- // Check if the file already exists in the bucket
197- try {
198- await this . minioClient . statObject ( this . s3ConfigService . bucket , key ) ;
199- this . logger . info ( 'File already exist. Skipping upload' , {
200- fileKey : key ,
201- } ) ;
202- return { url : this . getUrl ( key ) , hash } ;
203- } catch ( error ) {
204- if ( ! isNotFoundError ( error ) ) {
205- this . logger . error ( 'Error checking if file exists' , {
206- error,
207- fileKey : key ,
208- } ) ;
209- throw new Error ( 'Error accessing storage' ) ;
210- }
212+ const isAlreadyCopied = await this . checkFileExists ( key ) ;
213+ if ( isAlreadyCopied ) {
214+ return { url : copiedFileurl , hash } ;
211215 }
212216
213217 await this . minioClient . putObject (
@@ -220,18 +224,15 @@ export class StorageService {
220224 } ,
221225 ) ;
222226
223- return {
224- url : this . getUrl ( key ) ,
225- hash,
226- } ;
227+ return { url : copiedFileurl , hash } ;
227228 } catch ( error ) {
228229 this . logger . error ( 'Error copying file' , {
229230 error,
230- url ,
231+ originalFileUrl ,
231232 escrowAddress,
232233 chainId,
233234 } ) ;
234- throw new Error ( 'File not uploaded ' ) ;
235+ throw new Error ( 'File not copied ' ) ;
235236 }
236237 }
237238}
0 commit comments