Add S3Service with tests and module setup for MinIO integration
This commit is contained in:
137
backend/src/s3/s3.service.ts
Normal file
137
backend/src/s3/s3.service.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
import { Injectable, Logger, OnModuleInit } from "@nestjs/common";
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
import * as Minio from "minio";
|
||||
|
||||
@Injectable()
|
||||
export class S3Service implements OnModuleInit {
|
||||
private readonly logger = new Logger(S3Service.name);
|
||||
private minioClient: Minio.Client;
|
||||
private readonly bucketName: string;
|
||||
|
||||
constructor(private readonly configService: ConfigService) {
|
||||
this.minioClient = new Minio.Client({
|
||||
endPoint: this.configService.get<string>("S3_ENDPOINT", "localhost"),
|
||||
port: Number.parseInt(this.configService.get<string>("S3_PORT", "9000"), 10),
|
||||
useSSL: this.configService.get<string>("S3_USE_SSL") === "true",
|
||||
accessKey: this.configService.get<string>("S3_ACCESS_KEY", "minioadmin"),
|
||||
secretKey: this.configService.get<string>("S3_SECRET_KEY", "minioadmin"),
|
||||
});
|
||||
this.bucketName = this.configService.get<string>(
|
||||
"S3_BUCKET_NAME",
|
||||
"memegoat",
|
||||
);
|
||||
}
|
||||
|
||||
async onModuleInit() {
|
||||
await this.ensureBucketExists(this.bucketName);
|
||||
}
|
||||
|
||||
async ensureBucketExists(bucketName: string) {
|
||||
try {
|
||||
const exists = await this.minioClient.bucketExists(bucketName);
|
||||
if (!exists) {
|
||||
await this.minioClient.makeBucket(bucketName);
|
||||
this.logger.log(`Bucket "${bucketName}" created successfully.`);
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Error checking/creating bucket "${bucketName}": ${error.message}`,
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async uploadFile(
|
||||
fileName: string,
|
||||
file: Buffer,
|
||||
mimeType: string,
|
||||
metaData: Minio.ItemBucketMetadata = {},
|
||||
bucketName: string = this.bucketName,
|
||||
) {
|
||||
try {
|
||||
await this.minioClient.putObject(bucketName, fileName, file, file.length, {
|
||||
...metaData,
|
||||
"Content-Type": mimeType,
|
||||
});
|
||||
return fileName;
|
||||
} catch (error) {
|
||||
this.logger.error(`Error uploading file to ${bucketName}: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async getFile(fileName: string, bucketName: string = this.bucketName) {
|
||||
try {
|
||||
return await this.minioClient.getObject(bucketName, fileName);
|
||||
} catch (error) {
|
||||
this.logger.error(`Error getting file from ${bucketName}: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async getFileUrl(
|
||||
fileName: string,
|
||||
expiry = 3600,
|
||||
bucketName: string = this.bucketName,
|
||||
) {
|
||||
try {
|
||||
return await this.minioClient.presignedUrl(
|
||||
"GET",
|
||||
bucketName,
|
||||
fileName,
|
||||
expiry,
|
||||
);
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Error getting file URL from ${bucketName}: ${error.message}`,
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async deleteFile(fileName: string, bucketName: string = this.bucketName) {
|
||||
try {
|
||||
await this.minioClient.removeObject(bucketName, fileName);
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Error deleting file from ${bucketName}: ${error.message}`,
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async getFileInfo(fileName: string, bucketName: string = this.bucketName) {
|
||||
try {
|
||||
return await this.minioClient.statObject(bucketName, fileName);
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Error getting file info from ${bucketName}: ${error.message}`,
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async moveFile(
|
||||
sourceFileName: string,
|
||||
destinationFileName: string,
|
||||
sourceBucketName: string = this.bucketName,
|
||||
destinationBucketName: string = this.bucketName,
|
||||
) {
|
||||
try {
|
||||
const conds = new Minio.CopyConditions();
|
||||
await this.minioClient.copyObject(
|
||||
destinationBucketName,
|
||||
destinationFileName,
|
||||
`/${sourceBucketName}/${sourceFileName}`,
|
||||
conds,
|
||||
);
|
||||
await this.minioClient.removeObject(sourceBucketName, sourceFileName);
|
||||
return destinationFileName;
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Error moving file from ${sourceBucketName}/${sourceFileName} to ${destinationBucketName}/${destinationFileName}: ${error.message}`,
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user