import { Injectable, Logger, InternalServerErrorException } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { S3Client, PutObjectCommand, GetObjectCommand } from '@aws-sdk/client-s3'; import { S3File } from '../interfaces/s3-file.interface'; @Injectable() export class S3Service { private readonly s3Client: S3Client; private readonly logger = new Logger(S3Service.name); constructor(private readonly configService: ConfigService) { this.s3Client = new S3Client({ region: this.configService.get('AWS_REGION'), credentials: { accessKeyId: this.configService.get('AWS_ACCESS_KEY_ID'), secretAccessKey: this.configService.get('AWS_SECRET_ACCESS_KEY'), }, endpoint: this.configService.get('AWS_ENDPOINT_URL'), forcePathStyle: true, // Required for Contabo Object Storage }); } async uploadFile(file: Express.Multer.File, folder: string): Promise { try { const key = `${folder}/${Date.now()}-${file.originalname}`; const command = new PutObjectCommand({ Bucket: this.configService.get('AWS_S3_BUCKET_NAME'), Key: key, Body: file.buffer, ContentType: file.mimetype, }); await this.s3Client.send(command); this.logger.debug(`File uploaded successfully: ${key}`); return key; } catch (error) { this.logger.error(`Error uploading file to S3: ${error.message}`); throw new InternalServerErrorException('Failed to upload file to storage'); } } async getFile(key: string): Promise { try { const command = new GetObjectCommand({ Bucket: this.configService.get('AWS_S3_BUCKET_NAME'), Key: key, }); const response = await this.s3Client.send(command); const chunks = []; for await (const chunk of response.Body as any) { chunks.push(chunk); } const buffer = Buffer.concat(chunks); return { buffer, contentType: response.ContentType || 'application/octet-stream', contentLength: response.ContentLength || buffer.length, fileName: key.split('/').pop() || 'download', }; } catch (error) { this.logger.error(`Error getting file from S3: ${error.message}`); throw new InternalServerErrorException('Failed to retrieve file from storage'); } } }