132 lines
3.8 KiB
TypeScript
132 lines
3.8 KiB
TypeScript
import { CACHE_MANAGER } from '@nestjs/cache-manager';
|
|
import { Cache } from 'cache-manager';
|
|
import { Inject, Injectable } from '@nestjs/common';
|
|
import { ConfigService } from '@nestjs/config';
|
|
import { Client, ItemBucketMetadata, UploadedObjectInfo } from 'minio';
|
|
|
|
@Injectable()
|
|
export class MinioService {
|
|
private readonly client: Client;
|
|
public readonly defaultBucketName: string;
|
|
|
|
constructor(
|
|
private readonly configService: ConfigService,
|
|
@Inject(CACHE_MANAGER) private cacheManager: Cache,
|
|
) {
|
|
this.client = new Client({
|
|
endPoint: this.configService.get<string>('S3_ENDPOINT', 's3.hooli.co'),
|
|
port: Number(this.configService.get<number>('S3_PORT', 443)),
|
|
useSSL: this.configService.get<string>('S3_USE_SSL', 'true') === 'true',
|
|
accessKey: this.configService.get<string>('S3_ACCESS_KEY', ''),
|
|
secretKey: this.configService.get<string>('S3_SECRET_KEY', ''),
|
|
});
|
|
this.defaultBucketName = this.configService.get<string>('S3_BUCKET', '');
|
|
}
|
|
|
|
public async uploadFile(
|
|
bucketName: string,
|
|
objectName: string,
|
|
filePath: string,
|
|
metadata?: ItemBucketMetadata,
|
|
): Promise<UploadedObjectInfo> {
|
|
return await this.client.fPutObject(
|
|
bucketName,
|
|
objectName,
|
|
filePath,
|
|
metadata,
|
|
);
|
|
}
|
|
|
|
public async uploadBuffer(
|
|
bucketName: string,
|
|
objectName: string,
|
|
buffer: Buffer,
|
|
metadata?: ItemBucketMetadata,
|
|
): Promise<UploadedObjectInfo> {
|
|
return await this.client.putObject(
|
|
bucketName,
|
|
objectName,
|
|
buffer,
|
|
metadata,
|
|
);
|
|
}
|
|
|
|
public async getCachedBuffer(
|
|
bucketName: string,
|
|
objectName: string,
|
|
cacheTtl: number = 0.1,
|
|
): Promise<Buffer> {
|
|
const cacheKey = `${bucketName}/${objectName}`;
|
|
const cachedValue = await this.cacheManager.get<Buffer>(cacheKey);
|
|
if (cachedValue) {
|
|
return Buffer.from(cachedValue);
|
|
}
|
|
const object = await this.getBuffer(bucketName, objectName);
|
|
this.cacheManager.set(cacheKey, object, cacheTtl);
|
|
return object;
|
|
}
|
|
|
|
public async getBuffer(
|
|
bucketName: string,
|
|
objectName: string,
|
|
): Promise<Buffer> {
|
|
const objectStream = await this.client.getObject(bucketName, objectName);
|
|
return await new Promise((resolve, reject) => {
|
|
const chunks: Buffer[] = [];
|
|
objectStream.on('data', (chunk) => chunks.push(chunk));
|
|
objectStream.on('end', () => resolve(Buffer.concat(chunks)));
|
|
objectStream.on('error', (err) => reject(err));
|
|
});
|
|
}
|
|
|
|
public async getObjectMetadata(
|
|
bucketName: string,
|
|
objectName: string,
|
|
): Promise<ItemBucketMetadata> {
|
|
return await this.client.statObject(bucketName, objectName);
|
|
}
|
|
|
|
public async listBucketObjects(
|
|
bucketName: string,
|
|
prefix?: string,
|
|
recursive: boolean = false,
|
|
): Promise<string[]> {
|
|
const objectStream = await this.client.listObjectsV2(bucketName, prefix, recursive);
|
|
const objects = await new Promise<ItemBucketMetadata[]>(
|
|
(resolve, reject) => {
|
|
const objects: ItemBucketMetadata[] = [];
|
|
objectStream.on('data', (object) => objects.push(object));
|
|
objectStream.on('end', () => resolve(objects));
|
|
objectStream.on('error', (err) => reject(err));
|
|
},
|
|
);
|
|
return objects.map((object) => object.name);
|
|
}
|
|
|
|
public async deleteObject(
|
|
bucketName: string,
|
|
objectName: string,
|
|
): Promise<void> {
|
|
await this.client.removeObject(bucketName, objectName);
|
|
}
|
|
|
|
public async makeBucket(bucketName: string): Promise<void> {
|
|
return this.client.makeBucket(bucketName);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param bucketName
|
|
* @param objectName
|
|
* @param expiry Expiry in seconds
|
|
* @returns
|
|
*/
|
|
public async generatePresignedUrl(
|
|
bucketName: string,
|
|
objectName: string,
|
|
expiry: number = 60 * 60 * 24,
|
|
): Promise<string> {
|
|
return await this.client.presignedGetObject(bucketName, objectName, expiry);
|
|
}
|
|
}
|