Update code via agent code generation
This commit is contained in:
parent
a0aec8bfae
commit
939a1edbf6
@ -50,6 +50,8 @@
|
|||||||
"uuid": "^9.0.0",
|
"uuid": "^9.0.0",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"firebase-admin": "^11.10.1",
|
"firebase-admin": "^11.10.1",
|
||||||
|
"@aws-sdk/client-s3": "^3.450.0",
|
||||||
|
"@aws-sdk/s3-request-presigner": "^3.450.0",
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
"rxjs": "^7.8.1"
|
"rxjs": "^7.8.1"
|
||||||
},
|
},
|
||||||
|
161
src/aws/aws-s3.service.ts
Normal file
161
src/aws/aws-s3.service.ts
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
import { S3Client, PutObjectCommand, DeleteObjectCommand, GetObjectCommand } from '@aws-sdk/client-s3';
|
||||||
|
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AwsS3Service {
|
||||||
|
private readonly logger = new Logger(AwsS3Service.name);
|
||||||
|
private readonly s3Client: S3Client;
|
||||||
|
private readonly bucketName: string;
|
||||||
|
private readonly region: string;
|
||||||
|
|
||||||
|
constructor(private readonly configService: ConfigService) {
|
||||||
|
this.region = this.configService.get<string>('AWS_REGION') || 'us-east-1';
|
||||||
|
this.bucketName = this.configService.get<string>('AWS_S3_BUCKET_NAME');
|
||||||
|
|
||||||
|
if (!this.bucketName) {
|
||||||
|
throw new Error('AWS_S3_BUCKET_NAME environment variable is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.s3Client = new S3Client({
|
||||||
|
region: this.region,
|
||||||
|
credentials: {
|
||||||
|
accessKeyId: this.configService.get<string>('AWS_ACCESS_KEY_ID'),
|
||||||
|
secretAccessKey: this.configService.get<string>('AWS_SECRET_ACCESS_KEY'),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async uploadFile(
|
||||||
|
file: Express.Multer.File,
|
||||||
|
folder: string = 'uploads',
|
||||||
|
organizationId?: string,
|
||||||
|
): Promise<{ key: string; url: string }> {
|
||||||
|
try {
|
||||||
|
const fileExtension = file.originalname.split('.').pop();
|
||||||
|
const fileName = `${uuidv4()}.${fileExtension}`;
|
||||||
|
const key = organizationId
|
||||||
|
? `${folder}/${organizationId}/${fileName}`
|
||||||
|
: `${folder}/${fileName}`;
|
||||||
|
|
||||||
|
const command = new PutObjectCommand({
|
||||||
|
Bucket: this.bucketName,
|
||||||
|
Key: key,
|
||||||
|
Body: file.buffer,
|
||||||
|
ContentType: file.mimetype,
|
||||||
|
Metadata: {
|
||||||
|
originalName: file.originalname,
|
||||||
|
uploadedAt: new Date().toISOString(),
|
||||||
|
...(organizationId && { organizationId }),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.s3Client.send(command);
|
||||||
|
|
||||||
|
// Generate presigned URL for the uploaded file
|
||||||
|
const presignedUrl = await this.getPresignedUrl(key);
|
||||||
|
|
||||||
|
this.logger.log(`File uploaded successfully: ${key}`);
|
||||||
|
return { key, url: presignedUrl };
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`Failed to upload file: ${error.message}`);
|
||||||
|
throw new Error(`Failed to upload file: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPresignedUrl(key: string, expiresIn: number = 3600): Promise<string> {
|
||||||
|
try {
|
||||||
|
const command = new GetObjectCommand({
|
||||||
|
Bucket: this.bucketName,
|
||||||
|
Key: key,
|
||||||
|
});
|
||||||
|
|
||||||
|
const presignedUrl = await getSignedUrl(this.s3Client, command, {
|
||||||
|
expiresIn,
|
||||||
|
});
|
||||||
|
|
||||||
|
return presignedUrl;
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`Failed to generate presigned URL: ${error.message}`);
|
||||||
|
throw new Error(`Failed to generate presigned URL: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteFile(key: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
const command = new DeleteObjectCommand({
|
||||||
|
Bucket: this.bucketName,
|
||||||
|
Key: key,
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.s3Client.send(command);
|
||||||
|
this.logger.log(`File deleted successfully: ${key}`);
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`Failed to delete file: ${error.message}`);
|
||||||
|
throw new Error(`Failed to delete file: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async uploadAvatar(
|
||||||
|
file: Express.Multer.File,
|
||||||
|
userId: string,
|
||||||
|
organizationId?: string,
|
||||||
|
): Promise<{ key: string; url: string }> {
|
||||||
|
const folder = organizationId ? `avatars/${organizationId}` : 'avatars';
|
||||||
|
const key = `${folder}/${userId}-${uuidv4()}.${file.originalname.split('.').pop()}`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const command = new PutObjectCommand({
|
||||||
|
Bucket: this.bucketName,
|
||||||
|
Key: key,
|
||||||
|
Body: file.buffer,
|
||||||
|
ContentType: file.mimetype,
|
||||||
|
Metadata: {
|
||||||
|
userId,
|
||||||
|
type: 'avatar',
|
||||||
|
uploadedAt: new Date().toISOString(),
|
||||||
|
...(organizationId && { organizationId }),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.s3Client.send(command);
|
||||||
|
|
||||||
|
const presignedUrl = await this.getPresignedUrl(key);
|
||||||
|
|
||||||
|
this.logger.log(`Avatar uploaded successfully: ${key}`);
|
||||||
|
return { key, url: presignedUrl };
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`Failed to upload avatar: ${error.message}`);
|
||||||
|
throw new Error(`Failed to upload avatar: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async uploadChatMedia(
|
||||||
|
file: Express.Multer.File,
|
||||||
|
chatRoomId: string,
|
||||||
|
organizationId: string,
|
||||||
|
): Promise<{ key: string; url: string; type: string }> {
|
||||||
|
const mediaType = this.getMediaType(file.mimetype);
|
||||||
|
const folder = `chat-media/${organizationId}/${chatRoomId}/${mediaType}`;
|
||||||
|
|
||||||
|
return this.uploadFile(file, folder, organizationId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getMediaType(mimetype: string): string {
|
||||||
|
if (mimetype.startsWith('image/')) return 'images';
|
||||||
|
if (mimetype.startsWith('video/')) return 'videos';
|
||||||
|
if (mimetype.startsWith('audio/')) return 'audio';
|
||||||
|
if (mimetype.includes('pdf') || mimetype.includes('document')) return 'documents';
|
||||||
|
return 'files';
|
||||||
|
}
|
||||||
|
|
||||||
|
getBucketName(): string {
|
||||||
|
return this.bucketName;
|
||||||
|
}
|
||||||
|
|
||||||
|
getRegion(): string {
|
||||||
|
return this.region;
|
||||||
|
}
|
||||||
|
}
|
10
src/aws/aws.module.ts
Normal file
10
src/aws/aws.module.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { ConfigModule } from '@nestjs/config';
|
||||||
|
import { AwsS3Service } from './aws-s3.service';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [ConfigModule],
|
||||||
|
providers: [AwsS3Service],
|
||||||
|
exports: [AwsS3Service],
|
||||||
|
})
|
||||||
|
export class AwsModule {}
|
Loading…
x
Reference in New Issue
Block a user