import { BadRequestException, Body, Controller, Get, Param, Post, Query, Res, UploadedFile, UseInterceptors, UsePipes, } from '@nestjs/common'; import { FileInterceptor } from '@nestjs/platform-express'; import { ApiTags } from '@nestjs/swagger'; import { CurrentSessionUserId } from '../auth/current-session-user.decorator'; import { FilesService } from './files.service'; import { strictValidationPipe } from '../common/strict-validation.pipe'; import { ClientFileUploadDto } from './client-files.dto'; type UploadedMultipartFile = { originalname: string; mimetype: string; size: number; buffer: Buffer }; type DownloadResponse = { setHeader(name: string, value: number | string): void; send(content: Buffer): void }; @ApiTags('client-files') @Controller('client/files') export class ClientFilesController { constructor(private readonly files: FilesService) {} @Post('upload') @UseInterceptors(FileInterceptor('file', { limits: { fileSize: 10 * 1024 * 1024, files: 1, fields: 4, parts: 5 } })) @UsePipes(strictValidationPipe) upload( @CurrentSessionUserId() userId: string | undefined, @UploadedFile() file: UploadedMultipartFile, @Body() body: ClientFileUploadDto, ) { if (!file) throw new BadRequestException('Upload file is required'); return this.files.uploadForClient(userId, body, file); } @Get(':id/download') async download( @CurrentSessionUserId() userId: string | undefined, @Param('id') id: string, @Query('disposition') disposition: string | undefined, @Res() response: DownloadResponse, ) { const { fileObject, content } = await this.files.getClientDownload(userId, id); const mode = disposition === 'inline' ? 'inline' : 'attachment'; response.setHeader('Content-Type', fileObject.contentType || 'application/octet-stream'); response.setHeader('Content-Length', content.length); response.setHeader('Content-Disposition', `${mode}; filename*=UTF-8''${encodeURIComponent(fileObject.fileName)}`); response.send(content); } }