Skip to content

Commit 37fcb69

Browse files
committed
refactor: dry checking if file exists
1 parent e27dbc5 commit 37fcb69

File tree

4 files changed

+64
-71
lines changed

4 files changed

+64
-71
lines changed

packages/apps/reputation-oracle/server/src/common/errors/minio.ts

Lines changed: 0 additions & 7 deletions
This file was deleted.

packages/apps/reputation-oracle/server/src/common/interfaces/s3.ts

Lines changed: 0 additions & 4 deletions
This file was deleted.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export enum MinioErrorCodes {
2+
NotFound = 'NotFound',
3+
}

packages/apps/reputation-oracle/server/src/modules/storage/storage.service.ts

Lines changed: 61 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,29 @@ import { Injectable } from '@nestjs/common';
99
import crypto from 'crypto';
1010
import * as Minio from 'minio';
1111

12-
import { isNotFoundError } from '../../common/errors/minio';
13-
import { UploadedFile } from '../../common/interfaces/s3';
1412
import { FortuneFinalResult } from '../../common/interfaces/job-result';
1513
import { PGPConfigService } from '../../config/pgp-config.service';
1614
import { S3ConfigService } from '../../config/s3-config.service';
1715
import logger from '../../logger';
1816
import * as httpUtils from '../../utils/http';
1917

2018
import { 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()
2327
export 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

Comments
 (0)