Files
memegoat/backend/src/s3/s3.service.ts
Mathis HERRIOT 514bd354bf feat: add modular services and repositories for improved code organization
Introduce repository pattern across multiple services, including `favorites`, `tags`, `sessions`, `reports`, `auth`, and more. Decouple crypto functionalities into modular services like `HashingService`, `JwtService`, and `EncryptionService`. Improve testability and maintainability by simplifying dependencies and consolidating utility logic.
2026-01-14 12:11:39 +01:00

159 lines
4.1 KiB
TypeScript

import { Injectable, Logger, OnModuleInit } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import * as Minio from "minio";
import type { IStorageService } from "../common/interfaces/storage.interface";
@Injectable()
export class S3Service implements OnModuleInit, IStorageService {
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 getUploadUrl(
fileName: string,
expiry = 3600,
bucketName: string = this.bucketName,
) {
try {
return await this.minioClient.presignedUrl(
"PUT",
bucketName,
fileName,
expiry,
);
} catch (error) {
this.logger.error(
`Error getting upload URL for ${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;
}
}
}