feat: Implement pre-signed URL generation for message attachments with dedicated DTO, controller endpoint, and service logic.
This commit is contained in:
@@ -6,6 +6,7 @@ export enum UploadFolder {
|
|||||||
DOCUMENTS = 'documents',
|
DOCUMENTS = 'documents',
|
||||||
PROPERTIES = 'properties',
|
PROPERTIES = 'properties',
|
||||||
VERIFICATION = 'verification',
|
VERIFICATION = 'verification',
|
||||||
|
MESSAGES = 'messages',
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PresignedUrlDto {
|
export class PresignedUrlDto {
|
||||||
@@ -52,6 +53,22 @@ export class PresignedUrlResponseDto {
|
|||||||
key: string;
|
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 {
|
export class AvatarPresignedUrlDto {
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
example: 'profile.jpg',
|
example: 'profile.jpg',
|
||||||
|
|||||||
@@ -20,7 +20,8 @@ import {
|
|||||||
} from '@nestjs/swagger';
|
} from '@nestjs/swagger';
|
||||||
import type { Response } from 'express';
|
import type { Response } from 'express';
|
||||||
import { UploadService } from './upload.service';
|
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 { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
|
||||||
import { RolesGuard } from '../auth/guards/roles.guard';
|
import { RolesGuard } from '../auth/guards/roles.guard';
|
||||||
import { Roles } from '../auth/decorators/roles.decorator';
|
import { Roles } from '../auth/decorators/roles.decorator';
|
||||||
@@ -53,6 +54,27 @@ export class UploadController {
|
|||||||
return this.uploadService.getPresignedUrl(dto);
|
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<PresignedUrlResponseDto> {
|
||||||
|
return this.uploadService.getPresignedUrl({
|
||||||
|
filename: dto.filename,
|
||||||
|
contentType: dto.contentType,
|
||||||
|
folder: UploadFolder.MESSAGES,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Post('avatar-presigned-url')
|
@Post('avatar-presigned-url')
|
||||||
@UseGuards(JwtAuthGuard, RolesGuard)
|
@UseGuards(JwtAuthGuard, RolesGuard)
|
||||||
@Roles(UserRole.AGENT)
|
@Roles(UserRole.AGENT)
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ export class UploadService {
|
|||||||
[UploadFolder.DOCUMENTS]: 7 * 24 * 60 * 60, // 7 days
|
[UploadFolder.DOCUMENTS]: 7 * 24 * 60 * 60, // 7 days
|
||||||
[UploadFolder.PROPERTIES]: 30 * 24 * 60 * 60, // 30 days
|
[UploadFolder.PROPERTIES]: 30 * 24 * 60 * 60, // 30 days
|
||||||
[UploadFolder.VERIFICATION]: 1 * 24 * 60 * 60, // 1 day
|
[UploadFolder.VERIFICATION]: 1 * 24 * 60 * 60, // 1 day
|
||||||
|
[UploadFolder.MESSAGES]: 30 * 24 * 60 * 60, // 30 days
|
||||||
};
|
};
|
||||||
return `public, max-age=${cacheDurations[folder] || 86400}`;
|
return `public, max-age=${cacheDurations[folder] || 86400}`;
|
||||||
}
|
}
|
||||||
@@ -92,6 +93,15 @@ export class UploadService {
|
|||||||
'image/jpeg',
|
'image/jpeg',
|
||||||
'image/png',
|
'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];
|
const allowed = allowedTypes[folder];
|
||||||
|
|||||||
Reference in New Issue
Block a user