import { Injectable, Logger } from '@nestjs/common'; import { S3Client, DeleteObjectCommand, GetObjectCommand, ListObjectsCommand, PutObjectCommand, } from '@aws-sdk/client-s3'; import { getSignedUrl } from '@aws-sdk/s3-request-presigner'; import { Upload } from '@aws-sdk/lib-storage'; import { ConfigService } from '@nestjs/config'; @Injectable() export class S3Service { private s3Client: S3Client; private readonly logger = new Logger(S3Service.name); constructor(private configService: ConfigService) { this.s3Client = new S3Client({ region: this.configService.get('AWS_REGION'), endpoint: this.configService.get('AWS_ENDPOINT_URL'), credentials: { accessKeyId: this.configService.get('AWS_ACCESS_KEY_ID'), secretAccessKey: this.configService.get('AWS_SECRET_ACCESS_KEY'), }, forcePathStyle: true, // Needed for non-AWS S3 compatible services }); } async getObject(key: string) { try { const command = new GetObjectCommand({ Bucket: this.configService.get('AWS_S3_BUCKET_NAME'), Key: key, }); const response = await this.s3Client.send(command); return response; } catch (error) { this.logger.error(`Error getting object from S3: ${error.message}`); throw error; } } 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); return key; } catch (error) { this.logger.error(`Error uploading file to S3: ${error.message}`); throw error; } } async deleteFile(key: string): Promise { try { const command = new DeleteObjectCommand({ Bucket: this.configService.get('AWS_S3_BUCKET_NAME'), Key: key, }); await this.s3Client.send(command); } catch (error) { this.logger.error(`Error deleting file from S3: ${error.message}`); throw error; } } async getFileUrl(key: string): Promise { const command = new GetObjectCommand({ Bucket: this.configService.get('AWS_S3_BUCKET_NAME'), Key: key, }); const url = await getSignedUrl(this.s3Client, command, { expiresIn: 3600 }); // URL expires in 1 hour return url; } async testConnection(): Promise { try { await this.getObject('test-connection'); return true; } catch (error) { if (error.name === 'NoSuchKey') { // This is expected as we're just testing connection return true; } this.logger.error('Failed to connect to S3', error); return false; } } }