import { BadRequestException, Body, Controller, Delete, Get, HttpCode, Param, Post, Query, Res, UploadedFile, UseInterceptors, } from '@nestjs/common'; import { FileInterceptor } from '@nestjs/platform-express'; import { ApiBearerAuth, ApiConsumes, ApiTags } from '@nestjs/swagger'; import type { Response } from 'express'; import { DocumentEntityType, Role } from '../../generated/prisma'; import { Roles } from '../auth/decorators/roles.decorator'; import { CurrentUser } from '../auth/decorators/current-user.decorator'; import type { AuthenticatedUser } from '../auth/types/authenticated-user'; import { isAllowedDocumentMimeType } from './document-mime-types'; import { CreateDocumentDto } from './dto/create-document.dto'; import { DocumentsService } from './documents.service'; const MAX_FILE_SIZE_BYTES = 10 * 1024 * 1024; function sanitizeFilenameHeader(fileName: string): string { return fileName.replace(/[\r\n"]/g, '_'); } @ApiTags('documents') @ApiBearerAuth() @Controller('documents') export class DocumentsController { constructor(private readonly documentsService: DocumentsService) {} @Get() findAll( @Query('entityType') entityType: string, @Query('entityId') entityId: string, ) { if (!entityType || !entityId) { throw new BadRequestException('entityType and entityId are required'); } if ( !Object.values(DocumentEntityType).includes( entityType as DocumentEntityType, ) ) { throw new BadRequestException('Invalid entityType'); } return this.documentsService.findAll( entityType as DocumentEntityType, entityId, ); } @Roles(Role.ADMIN, Role.BEEKEEPER, Role.STAFF) @Post() @ApiConsumes('multipart/form-data') @UseInterceptors( FileInterceptor('file', { limits: { fileSize: MAX_FILE_SIZE_BYTES }, fileFilter: (_req, file, callback) => { if (!isAllowedDocumentMimeType(file.mimetype)) { callback( new BadRequestException('Nicht unterstützter Dateityp'), false, ); return; } callback(null, true); }, }), ) create( @UploadedFile() file: Express.Multer.File | undefined, @Body() dto: CreateDocumentDto, @CurrentUser() actor: AuthenticatedUser, ) { if (!file) { throw new BadRequestException('No file uploaded'); } return this.documentsService.create( file, dto.entityType, dto.entityId, actor.id, ); } @Get(':id/download') async download(@Param('id') id: string, @Res() res: Response): Promise { const { buffer, fileName, mimeType } = await this.documentsService.readFile(id); res.setHeader('Content-Type', mimeType); res.setHeader( 'Content-Disposition', `attachment; filename="${sanitizeFilenameHeader(fileName)}"`, ); res.send(buffer); } @Roles(Role.ADMIN, Role.BEEKEEPER, Role.STAFF) @Delete(':id') @HttpCode(204) delete(@Param('id') id: string, @CurrentUser() actor: AuthenticatedUser) { return this.documentsService.delete(id, actor.id); } }