import { readFile } from "node:fs/promises"; import { extname, parse } from "node:path"; import { BadRequestException, Body, Controller, Get, Param, Patch, Post, Put, Query, UploadedFile, UseGuards, UseInterceptors, } from "@nestjs/common"; import { FileInterceptor } from "@nestjs/platform-express"; import { ApiBearerAuth, ApiBody, ApiConsumes, ApiParam, ApiQuery, ApiTags, ApiOperation, } from "@nestjs/swagger"; import { diskStorage } from "multer"; import { ClaimAccessGuard } from "src/auth/guards/claim-access.guard"; import { RolesGuard } from "src/auth/guards/role.guard"; import { Roles } from "src/decorators/roles.decorator"; import { CurrentUser } from "src/decorators/user.decorator"; import { RoleEnum } from "src/Types&Enums/role.enum"; import { DEFAULT_MEDIA_MAX_BYTES } from "src/client/client.service"; import { ClaimRequestManagementService } from "./claim-request-management.service"; import { ClaimRequiredDocumentType } from "src/Types&Enums/claim-request-management/required-document-type.enum"; import { CarDamagePartDto, OtherCarDamagePartDto } from "./dto/car-part.dto"; import { UserCommentDto } from "./dto/user-comment.dto"; import { UserObjectionDto } from "./dto/user-objection.dto"; import { InPersonVisitDto } from "./dto/in-person-visit.dto"; import { UserRatingDto } from "./dto/user-rating.dto"; @Controller("claim-request-management") @ApiTags("claim-request-management") @Roles(RoleEnum.USER, RoleEnum.EXPERT, RoleEnum.DAMAGE_EXPERT) @UseGuards(ClaimAccessGuard, RolesGuard) @ApiBearerAuth() export class ClaimRequestManagementController { constructor( private readonly claimRequestManagementService: ClaimRequestManagementService, ) {} @ApiParam({ name: "blameId" }) @ApiOperation({ deprecated: true }) @Post("/:blameId") async createClaimRequest( @Param("blameId") requestId: string, @CurrentUser() user, ) { return await this.claimRequestManagementService.createClaimRequest( requestId, user.role === RoleEnum.USER ? user.sub : undefined, user, ); } @ApiBody({ type: CarDamagePartDto }) @ApiOperation({ deprecated: true }) @Patch("/car-part-damage/:claimRequestID") @ApiParam({ name: "claimRequestID" }) async carPartDamage( @Param("claimRequestID") requestId: string, @Body() body: CarDamagePartDto, @CurrentUser() user, ) { return await this.claimRequestManagementService.selectCarPartDamage( requestId, body, user, ); } @ApiOperation({ deprecated: true }) @Get("/car-other-part") async getCarOtherParts() { const carOtherPart = await readFile( `${process.cwd()}/src/static/car-part.json`, "utf-8", ); return carOtherPart; } @ApiBody({ type: OtherCarDamagePartDto }) @ApiParam({ name: "claimRequestID" }) @UseInterceptors( FileInterceptor("file", { limits: { fileSize: DEFAULT_MEDIA_MAX_BYTES, }, storage: diskStorage({ destination: "./files/car-green-cards", filename: (req, file, callback) => { const unique = Date.now(); const ex = extname(file.originalname); const filename = `${file.originalname.split(" ")[0]}-${unique}${ex}`; callback(null, filename); }, }), }), ) @ApiConsumes("multipart/form-data") @ApiOperation({ deprecated: true }) @Patch("/car-other-part-damage/:claimRequestID") async carOtherPartDamage( @Param("claimRequestID") requestId: string, @UploadedFile() file, @Body() body: OtherCarDamagePartDto, @CurrentUser() user, ) { return await this.claimRequestManagementService.selectCarOtherPartDamage( requestId, body, file, user, ); } @ApiOperation({ deprecated: true }) @Get("required-documents-status/:claimRequestID") @ApiParam({ name: "claimRequestID" }) async getRequiredDocumentsStatus( @Param("claimRequestID") requestId: string, ) { return await this.claimRequestManagementService.getRequiredDocumentsStatus( requestId, ); } @ApiOperation({ deprecated: true }) @Get("car-part-image-required/:claimRequestID") @ApiParam({ name: "claimRequestID" }) async getImageRequired(@Param("claimRequestID") requestId) { return await this.claimRequestManagementService.getImageRequiredList( requestId, ); } @ApiBody({ schema: { type: "object", properties: { file: { type: "string", format: "binary" }, }, }, }) @UseInterceptors( FileInterceptor("file", { limits: { fileSize: DEFAULT_MEDIA_MAX_BYTES }, storage: diskStorage({ destination: "./files/claim-required-documents/", filename: (req, file, callback) => { const extension = extname(file.originalname); const basename = parse(file.originalname).name; const unique = Date.now(); const sanitizedBasename = basename .replace(/\s/g, "_") .replace(/[^\w\-_]/g, "") .substring(0, 50); const filename = `${sanitizedBasename}-${unique}${extension}`; callback(null, filename); }, }), }), ) @ApiConsumes("multipart/form-data") @ApiParam({ name: "claimRequestID" }) @ApiQuery({ name: "documentType", enum: ClaimRequiredDocumentType, description: "Type of required document to upload", }) @ApiOperation({ deprecated: true }) @Patch("upload-required-document/:claimRequestID") async uploadRequiredDocument( @Param("claimRequestID") requestId: string, @Query("documentType") documentType: string, @UploadedFile("file") file: Express.Multer.File, @CurrentUser() user, ) { if (!file) { throw new BadRequestException("File is required"); } return await this.claimRequestManagementService.uploadRequiredDocument( requestId, documentType as any, file, user, ); } @ApiBody({ schema: { type: "object", properties: { file: { type: "string", format: "binary" }, }, }, }) @UseInterceptors( FileInterceptor("file", { limits: { fileSize: DEFAULT_MEDIA_MAX_BYTES }, storage: diskStorage({ destination: "./files/car-parts/", filename: (req, file, callback) => { const extension = extname(file.originalname); const basename = parse(file.originalname).name; const unique = Date.now(); // Sanitize filename: remove non-ASCII characters and special chars to avoid encoding issues const sanitizedBasename = basename .replace(/\s/g, "_") .replace(/[^\w\-_]/g, "") // Remove all non-word characters except hyphens and underscores .substring(0, 50); // Limit length const filename = `${sanitizedBasename}-${unique}${extension}`; callback(null, filename); }, }), }), ) @ApiConsumes("multipart/form-data") @ApiParam({ name: "claimRequestID" }) @ApiParam({ name: "partId", description: "The ID of the specific car part being photographed.", }) @ApiOperation({ deprecated: true }) @Patch("capture-car-part-damage/:claimRequestID/:partId") async captureCarPartDamage( @Param("partId") partId: string, @Param("claimRequestID") requestId: string, @UploadedFile("file") file: Express.Multer.File, ) { if (!file) { throw new BadRequestException("Image file is required."); } return await this.claimRequestManagementService.setDamageImage( requestId, partId, file, ); } @ApiBody({ schema: { type: "object", properties: { file: { type: "string", format: "binary" }, }, }, }) @UseInterceptors( FileInterceptor("file", { limits: { fileSize: DEFAULT_MEDIA_MAX_BYTES }, storage: diskStorage({ destination: "./files/car-capture-videos/", filename: (req, file, callback) => { const unique = Date.now(); const ex = extname(file.originalname); const filename = `claim-video-${unique}${ex}`; callback(null, filename); }, }), }), ) @ApiConsumes("multipart/form-data") @ApiParam({ name: "claimRequestID" }) @ApiOperation({ deprecated: true }) @Patch("car-capture/:claimRequestID") async captureVideoCapture( @Param("claimRequestID") requestId: string, @UploadedFile("file") file: Express.Multer.File, ) { return await this.claimRequestManagementService.setVideoCapture( requestId, file, ); } @ApiOperation({ deprecated: true }) @Get("requests/") async getRequest(@CurrentUser() currentUser) { return await this.claimRequestManagementService.myRequests(currentUser); } @ApiOperation({ deprecated: true }) @Get("request/:claimRequestId") @ApiParam({ name: "claimRequestId" }) myRequests( @Param("claimRequestId") requestId: string, @CurrentUser() user, ) { return this.claimRequestManagementService.requestDetails(requestId, user); } @ApiOperation({ deprecated: true }) @Put("request/reply/:claimRequestId") @ApiParam({ name: "claimRequestId" }) @UseInterceptors( FileInterceptor("file", { limits: { fileSize: DEFAULT_MEDIA_MAX_BYTES, }, storage: diskStorage({ destination: "./files/claim-sign", filename: (req, file, callback) => { const unique = Date.now(); const ex = extname(file.originalname); const filename = `${file.originalname.split(" ")[0]}-${unique}${ex}`; callback(null, filename); }, }), }), ) @ApiBody({ type: UserCommentDto, description: "if partId null , you can upload video capture", }) @ApiConsumes("multipart/form-data") @ApiParam({ name: "claimRequestId" }) async submitReply( @Param("claimRequestId") requestId, @Body() body, @UploadedFile() file: Express.Multer.File, @CurrentUser() user, ) { return await this.claimRequestManagementService.submitUserReply( requestId, body, file, user, ); } @ApiOperation({ deprecated: true }) @Put("request/resend/:claimRequestId/objection") @ApiParam({ name: "claimRequestId" }) @ApiConsumes("application/json") @ApiBody({ type: UserObjectionDto, description: "Objection details with optional new parts", }) async handleUserObjection( @Param("claimRequestId") claimRequestId: string, @Body() userObjectionDto: UserObjectionDto, ) { return await this.claimRequestManagementService.handleUserObjectionAndParts( claimRequestId, userObjectionDto, ); } @ApiOperation({ deprecated: true }) @Patch("request/resend/:claimRequestId") @ApiConsumes("multipart/form-data") @ApiParam({ name: "claimRequestId" }) @ApiQuery({ name: "fields", enum: ["resendDocuments", "resendCarParts"] }) @ApiQuery({ name: "partId", required: false }) @ApiQuery({ name: "documentName", required: false }) @ApiQuery({ name: "side", required: false }) @ApiBody({ schema: { type: "object", properties: { file: { type: "string", format: "binary", }, }, }, }) @UseInterceptors( FileInterceptor("file", { storage: diskStorage({ destination: "./files/claim-resend-documents", filename: (req, file, callback) => { const unique = Date.now(); const ext = extname(file.originalname); const filename = `${file.originalname.split(" ")[0]}-${unique}${ext}`; callback(null, filename); }, }), limits: { fileSize: DEFAULT_MEDIA_MAX_BYTES }, }), ) async uploadDocuments( @Param("claimRequestId") claimId: string, @Query() query: string, @UploadedFile() file: Express.Multer.File, @CurrentUser() user, ) { return await this.claimRequestManagementService.resendFiles( claimId, file, query, user, ); } /** * User satisfaction rating for a completed claim file. * Only the damaged user (claim owner) can rate their claim after it is closed. */ @ApiOperation({ deprecated: true }) @Put("request/:claimRequestId/user-rating") @ApiParam({ name: "claimRequestId" }) @ApiBody({ type: UserRatingDto }) async addUserRating( @Param("claimRequestId") claimRequestId: string, @Body() ratingDto: UserRatingDto, @CurrentUser() user, ) { return await this.claimRequestManagementService.addUserRating( claimRequestId, user, ratingDto, ); } @ApiOperation({ deprecated: true }) @Patch("request/reply/:claimRequestId/:partId/upload-factor") @ApiConsumes("multipart/form-data") @ApiParam({ name: "claimRequestId" }) @ApiParam({ name: "partId" }) @ApiBody({ schema: { type: "object", properties: { file: { type: "string", format: "binary", }, }, }, }) @UseInterceptors( FileInterceptor("file", { storage: diskStorage({ destination: "./files/claim-factors", filename: (req, file, callback) => { const unique = Date.now(); const filename = `-${unique}-${file.originalname}`; callback(null, filename); }, }), limits: { fileSize: DEFAULT_MEDIA_MAX_BYTES }, }), ) async uploadFactorForPart( @Param("claimRequestId") claimId: string, @Param("partId") partId: string, @UploadedFile() file: Express.Multer.File, @CurrentUser() user, ) { return await this.claimRequestManagementService.uploadClaimFactor( claimId, partId, file, user, ); } @ApiBody({ type: InPersonVisitDto }) @ApiParam({ name: "id" }) @ApiOperation({ deprecated: true }) @Patch(":id/visit") async inPersonVisit( @Param("id") requestId: string, @Body() body: InPersonVisitDto, @CurrentUser() actor, ) { // Pass the branchId from the body to the service return await this.claimRequestManagementService.inPersonVisit( requestId, body.branchId, actor, ); } @ApiOperation({ deprecated: true }) @Get("branches/:insuranceId") async insuranceBranches(@Param("insuranceId") insuranceId: string) { return await this.claimRequestManagementService.retrieveInsuranceBranches( insuranceId, ); } @ApiOperation({ deprecated: true }) @Get("fanavaran-submit/:claimRequestId") @ApiParam({ name: "claimRequestId" }) async fanavaranSubmit(@Param("claimRequestId") claimRequestId: string) { return await this.claimRequestManagementService.fanavaranSubmit( claimRequestId, ); } @ApiOperation({ deprecated: true }) @Post("fanavaran-submit/:claimRequestId") @ApiParam({ name: "claimRequestId" }) async submitToFanavaran(@Param("claimRequestId") claimRequestId: string) { return await this.claimRequestManagementService.submitToFanavaran( claimRequestId, ); } }