import { BadRequestException, Body, Controller, Get, Param, Post, Query, Res, UploadedFile, UseInterceptors } from '@nestjs/common'; import { FileInterceptor } from '@nestjs/platform-express'; import { ApiTags } from '@nestjs/swagger'; import { TenantId } from '../common/tenant-id.decorator'; import { CreateFileObjectDto, CreatePresignedUploadDto, FilesService } from './files.service'; type UploadedMultipartFile = { originalname: string; mimetype: string; size: number; buffer: Buffer; }; type DownloadResponse = { setHeader(name: string, value: number | string): void; send(content: Buffer): void; }; @ApiTags('files') @Controller('admin/files') export class FilesController { constructor(private readonly files: FilesService) {} @Get() list(@TenantId() tenantId?: string) { return this.files.list(tenantId); } @Get(':id/download') async download(@Param('id') id: string, @Query('disposition') disposition: string | undefined, @Res() response: DownloadResponse) { const { fileObject, content } = await this.files.getDownload(id); const mode = disposition === 'inline' ? 'inline' : 'attachment'; const encodedName = encodeURIComponent(fileObject.fileName); response.setHeader('Content-Type', fileObject.contentType || 'application/octet-stream'); response.setHeader('Content-Length', content.length); response.setHeader('Content-Disposition', `${mode}; filename*=UTF-8''${encodedName}`); response.send(content); } @Post() create(@Body() body: CreateFileObjectDto) { return this.files.create(body); } @Post('presigned-upload') createPresignedUpload(@Body() body: CreatePresignedUploadDto) { return this.files.createPresignedUpload(body); } @Post('upload') @UseInterceptors(FileInterceptor('file', { limits: { fileSize: 20 * 1024 * 1024, files: 1, fields: 4, parts: 5, fieldNameSize: 100, fieldSize: 1024, headerPairs: 100, }, })) upload(@UploadedFile() file: UploadedMultipartFile, @Body('purpose') purpose: string, @Body('prefix') prefix?: string, @TenantId() tenantId?: string) { if (!file) { throw new BadRequestException('Upload file is required'); } return this.files.upload({ tenantId, purpose: purpose || 'general', prefix }, file); } }