import { Body, Controller, Get, Param, ParseUUIDPipe, Post, Req, Res, UploadedFile, UseInterceptors } from '@nestjs/common'; import { FileInterceptor } from '@nestjs/platform-express'; import type { Response } from 'express'; import { CurrentAuth } from '../auth/decorators/current-auth.decorator'; import { RequirePermissions } from '../authorization/decorators/require-permissions.decorator'; import type { AuthPrincipal, RequestWithContext } from '../common/http/request-context'; import { ApproveInspectionReportDto } from './dto/approve-inspection-report.dto'; import { CreateInspectionReportRevisionDto } from './dto/create-inspection-report-revision.dto'; import { SignFinalInspectionReportDto } from './dto/sign-final-inspection-report.dto'; import { MAX_INSPECTION_REPORT_REVISION_BYTES, type UploadedInspectionReportRevisionFile, } from './inspection-report-revision-file'; import { InspectionReportReviewService, type InspectionReportReviewView, } from './inspection-report-review.service'; @Controller('inspection-reports/:reportId/review') export class InspectionReportReviewController { constructor(private readonly review: InspectionReportReviewService) {} @Get() @RequirePermissions('inspection_reports.read') get(@Param('reportId', new ParseUUIDPipe({ version: '4' })) reportId: string): Promise { return this.review.get(reportId); } @Post('revisions') @RequirePermissions('inspection_reports.revise') @UseInterceptors(FileInterceptor('file', { limits: { fileSize: MAX_INSPECTION_REPORT_REVISION_BYTES, files: 1 } })) revision( @Param('reportId', new ParseUUIDPipe({ version: '4' })) reportId: string, @Body() dto: CreateInspectionReportRevisionDto, @UploadedFile() file: UploadedInspectionReportRevisionFile | undefined, @CurrentAuth() principal: AuthPrincipal, @Req() request: RequestWithContext, ): Promise { return this.review.createRevision(reportId, dto, file, principal, request); } @Post('approve') @RequirePermissions('inspection_reports.review') approve( @Param('reportId', new ParseUUIDPipe({ version: '4' })) reportId: string, @Body() dto: ApproveInspectionReportDto, @CurrentAuth() principal: AuthPrincipal, @Req() request: RequestWithContext, ): Promise { return this.review.approve(reportId, dto, principal, request); } @Post('sign-final') @RequirePermissions('inspection_reports.sign_final') signFinal( @Param('reportId', new ParseUUIDPipe({ version: '4' })) reportId: string, @Body() dto: SignFinalInspectionReportDto, @CurrentAuth() principal: AuthPrincipal, @Req() request: RequestWithContext, ): Promise { return this.review.signFinal(reportId, dto, principal, request); } } @Controller('inspection-report-revisions') export class InspectionReportRevisionContentController { constructor(private readonly review: InspectionReportReviewService) {} @Get(':revisionId/content') @RequirePermissions('inspection_reports.read') async content( @Param('revisionId', new ParseUUIDPipe({ version: '4' })) revisionId: string, @Res() response: Response, ): Promise { const content = await this.review.revisionContent(revisionId); response.setHeader('Content-Type', content.mimeType); response.setHeader('Content-Disposition', `attachment; filename="${content.originalName.replaceAll('"', '')}"`); response.setHeader('Cache-Control', 'private, no-store'); response.setHeader('X-Content-Type-Options', 'nosniff'); await new Promise((resolveSend, rejectSend) => { response.sendFile(content.filePath, (error) => { if (error) rejectSend(error); else resolveSend(); }); }); } }