From dd6636dbb3b6cb94e4d33b87b4d750f7ec7739bb Mon Sep 17 00:00:00 2001 From: pradeepkumar Date: Sat, 21 Feb 2026 22:12:19 +0530 Subject: [PATCH] feat: Implement pre-signed URL generation for message attachments with dedicated DTO, controller endpoint, and service logic. --- src/upload/dto/upload.dto.ts | 17 +++++++++++++++++ src/upload/upload.controller.ts | 24 +++++++++++++++++++++++- src/upload/upload.service.ts | 10 ++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/upload/dto/upload.dto.ts b/src/upload/dto/upload.dto.ts index 0cb9f57..882dce2 100644 --- a/src/upload/dto/upload.dto.ts +++ b/src/upload/dto/upload.dto.ts @@ -6,6 +6,7 @@ export enum UploadFolder { DOCUMENTS = 'documents', PROPERTIES = 'properties', VERIFICATION = 'verification', + MESSAGES = 'messages', } export class PresignedUrlDto { @@ -52,6 +53,22 @@ export class PresignedUrlResponseDto { key: string; } +export class MessagePresignedUrlDto { + @ApiProperty({ + example: 'photo.jpg', + description: 'Original filename', + }) + @IsString() + filename: string; + + @ApiProperty({ + example: 'image/jpeg', + description: 'MIME type of the file', + }) + @IsString() + contentType: string; +} + export class AvatarPresignedUrlDto { @ApiProperty({ example: 'profile.jpg', diff --git a/src/upload/upload.controller.ts b/src/upload/upload.controller.ts index 77cf26f..1249774 100644 --- a/src/upload/upload.controller.ts +++ b/src/upload/upload.controller.ts @@ -20,7 +20,8 @@ import { } from '@nestjs/swagger'; import type { Response } from 'express'; import { UploadService } from './upload.service'; -import { PresignedUrlDto, PresignedUrlResponseDto, AvatarPresignedUrlDto } from './dto'; +import { PresignedUrlDto, PresignedUrlResponseDto, AvatarPresignedUrlDto, MessagePresignedUrlDto } from './dto'; +import { UploadFolder } from './dto'; import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard'; import { RolesGuard } from '../auth/guards/roles.guard'; import { Roles } from '../auth/decorators/roles.decorator'; @@ -53,6 +54,27 @@ export class UploadController { return this.uploadService.getPresignedUrl(dto); } + @Post('message-presigned-url') + @UseGuards(JwtAuthGuard, RolesGuard) + @Roles(UserRole.USER, UserRole.AGENT) + @ApiBearerAuth() + @ApiOperation({ summary: 'Get a pre-signed URL for message attachment upload (images and documents)' }) + @ApiResponse({ + status: 200, + description: 'Pre-signed URL generated successfully', + type: PresignedUrlResponseDto, + }) + @ApiResponse({ status: 400, description: 'Invalid content type' }) + async getMessagePresignedUrl( + @Body() dto: MessagePresignedUrlDto, + ): Promise { + return this.uploadService.getPresignedUrl({ + filename: dto.filename, + contentType: dto.contentType, + folder: UploadFolder.MESSAGES, + }); + } + @Post('avatar-presigned-url') @UseGuards(JwtAuthGuard, RolesGuard) @Roles(UserRole.AGENT) diff --git a/src/upload/upload.service.ts b/src/upload/upload.service.ts index 5626367..fb25f7c 100644 --- a/src/upload/upload.service.ts +++ b/src/upload/upload.service.ts @@ -66,6 +66,7 @@ export class UploadService { [UploadFolder.DOCUMENTS]: 7 * 24 * 60 * 60, // 7 days [UploadFolder.PROPERTIES]: 30 * 24 * 60 * 60, // 30 days [UploadFolder.VERIFICATION]: 1 * 24 * 60 * 60, // 1 day + [UploadFolder.MESSAGES]: 30 * 24 * 60 * 60, // 30 days }; return `public, max-age=${cacheDurations[folder] || 86400}`; } @@ -92,6 +93,15 @@ export class UploadService { 'image/jpeg', 'image/png', ], + [UploadFolder.MESSAGES]: [ + 'image/jpeg', + 'image/png', + 'image/webp', + 'image/gif', + 'application/pdf', + 'application/msword', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + ], }; const allowed = allowedTypes[folder];