/** * Route handlers for `/api/v1/berths/[id]/pdf-versions` (Phase 6b). * * Lives in handlers.ts (not route.ts) so integration tests can call them * directly, bypassing the auth/permission middleware (per CLAUDE.md * "Route handler exports" convention). */ import { NextResponse } from 'next/server'; import { type RouteHandler } from '@/lib/api/helpers'; import { errorResponse, ValidationError } from '@/lib/errors'; import { listBerthPdfVersions, uploadBerthPdf } from '@/lib/services/berth-pdf.service'; interface PostBody { storageKey: string; fileName: string; fileSizeBytes: number; sha256: string; parseResults?: { engine: 'acroform' | 'ocr' | 'ai'; extracted?: Record; meanConfidence?: number; warnings?: string[]; }; } export const getHandler: RouteHandler = async (_req, _ctx, params) => { try { const versions = await listBerthPdfVersions(params.id!); return NextResponse.json({ data: versions }); } catch (error) { return errorResponse(error); } }; export const postHandler: RouteHandler = async (req, ctx, params) => { try { const body = (await req.json()) as Partial; if (!body.storageKey || !body.fileName) { throw new ValidationError('storageKey and fileName are required'); } if (typeof body.fileSizeBytes !== 'number' || body.fileSizeBytes <= 0) { throw new ValidationError('fileSizeBytes must be a positive integer'); } if (!body.sha256 || typeof body.sha256 !== 'string') { throw new ValidationError('sha256 is required'); } const result = await uploadBerthPdf({ berthId: params.id!, storageKey: body.storageKey, fileName: body.fileName, fileSizeBytes: body.fileSizeBytes, sha256: body.sha256, uploadedBy: ctx.userId, parseResult: body.parseResults ? { engine: body.parseResults.engine, // Reconstruct just enough of the ParseResult shape to round-trip // through serialization; the rep already saw the conflicts in the // diff dialog, so storing the engine + extracted is what we need // for audit. fields: Object.fromEntries( Object.entries(body.parseResults.extracted ?? {}).map(([k, v]) => { if (v && typeof v === 'object' && 'value' in v) { const obj = v as { value: unknown; confidence?: number }; return [ k, { value: obj.value as never, confidence: typeof obj.confidence === 'number' ? obj.confidence : 1, engine: body.parseResults!.engine, }, ]; } return [k, undefined]; }), ) as never, meanConfidence: body.parseResults.meanConfidence ?? 1, warnings: body.parseResults.warnings ?? [], } : undefined, }); return NextResponse.json({ data: result }, { status: 201 }); } catch (error) { return errorResponse(error); } };