diff --git a/apps/edr-freight-api/src/main.ts b/apps/edr-freight-api/src/main.ts index 5b027448c..20c21b984 100644 --- a/apps/edr-freight-api/src/main.ts +++ b/apps/edr-freight-api/src/main.ts @@ -2,6 +2,7 @@ import "reflect-metadata"; import * as dotenv from "dotenv"; dotenv.config(); import { NestFactory } from "@nestjs/core"; +import { json, urlencoded } from "express"; import { DocumentBuilder, SwaggerModule } from "@nestjs/swagger"; import { HttpExceptionFilter, @@ -11,9 +12,20 @@ import { import { AppModule } from "./app.module"; +/** + * JSON body ceiling. Signing posts the signature AND the company stamp as + * base64 in one JSON body, and base64 inflates bytes by ~4/3 — a 10MB stamp is + * ~13.4MB on the wire. Express defaults to 100kb, which rejected any real stamp + * image with a 413 "request entity too large". + */ +const JSON_BODY_LIMIT = '20mb'; + async function bootstrap() { const app = await NestFactory.create(AppModule); + app.use(json({ limit: JSON_BODY_LIMIT })); + app.use(urlencoded({ limit: JSON_BODY_LIMIT, extended: true })); + // Dev CORS: reflect any localhost origin and allow credentials so the // freight portal (5173), passenger portal (5174), backoffices (5183/5184) // and any other dev port can call the API with cookies + Authorization. diff --git a/apps/edr-freight-api/src/modules/contracts/contracts.controller.ts b/apps/edr-freight-api/src/modules/contracts/contracts.controller.ts index 9aa37a0a7..4d70d4203 100644 --- a/apps/edr-freight-api/src/modules/contracts/contracts.controller.ts +++ b/apps/edr-freight-api/src/modules/contracts/contracts.controller.ts @@ -276,6 +276,18 @@ export class ContractsController { return this.clearanceService.queue(filter); } + // Must stay ABOVE @Get(':id') — declared after it, Nest matched the literal + // path as an id and ParseUUIDPipe answered 400 "uuid is expected". + @Get('awaiting-shipment') + @BookingStaff(FREIGHT_PERMS.contracts.createBooking) + @ApiOperation({ + summary: + 'GL worklist: executed one-time customs contracts with no shipment instance yet — GL initiates the booking the customer then uploads documents on.', + }) + awaitingShipmentContracts() { + return this.contractBookingService.awaitingShipmentContracts(); + } + @Get(':id') @ApiOperation({ summary: 'Get contract by ID (routes, cargo scope, unit rates)' }) async findOne( @@ -986,16 +998,6 @@ export class ContractsController { return this.clearanceService.finalizeExportClearance(id, resolveAuthUserId(user)); } - @Get('awaiting-shipment') - @BookingStaff(FREIGHT_PERMS.contracts.createBooking) - @ApiOperation({ - summary: - 'GL worklist: executed one-time customs contracts with no shipment instance yet — GL initiates the booking the customer then uploads documents on.', - }) - awaitingShipmentContracts() { - return this.contractBookingService.awaitingShipmentContracts(); - } - // ── Path A self-clearance — Operations reviews the customer's own docs ─────── @Post(':id/clearance/ops-review') diff --git a/apps/edr-freight-web/portal/src/components/contracts/StampUpload.tsx b/apps/edr-freight-web/portal/src/components/contracts/StampUpload.tsx index 1c1e4d87c..bdf47c5fd 100644 --- a/apps/edr-freight-web/portal/src/components/contracts/StampUpload.tsx +++ b/apps/edr-freight-web/portal/src/components/contracts/StampUpload.tsx @@ -2,7 +2,7 @@ import { useRef, useState } from "react"; import { Box, Button, Group, Image, Paper, Stack, Text } from "@mantine/core"; import { RefreshCw, Stamp, X } from "lucide-react"; -const MAX_STAMP_MB = 5; +const MAX_STAMP_MB = 10; export interface StampUploadProps { /** Stamp image as a data URL, or null when none is attached yet. */