fix: coerce empty FormData strings to null for UUID columns (AIIA-59)
Some checks failed
Deploy to VPS / deploy (push) Has been cancelled

Document upload failed with "invalid input syntax for type uuid" when
decision_id or norm_instrument_id were sent as empty strings from
FormData. The ?? operator only catches null/undefined, not "".

Added emptyToUndefined() to sanitize all optional UUID fields at the
API boundary before they reach the DB insert.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
CTO
2026-04-10 07:43:43 +00:00
parent 23a66f92fc
commit 8172872329

View File

@@ -8,6 +8,12 @@ import { requirePermission } from '@/lib/auth/rbac';
const VALID_CATEGORIES = new Set(['entscheidung', 'norm', 'falldokument', 'sonstiges']);
/** Convert empty/whitespace-only strings to undefined (FormData sends "" for blank fields). */
function emptyToUndefined(value: string | null): string | undefined {
if (!value || value.trim() === '') return undefined;
return value;
}
export async function POST(request: NextRequest) {
const auth = await requirePermission('cases:edit');
if ('response' in auth) return auth.response;
@@ -16,9 +22,9 @@ export async function POST(request: NextRequest) {
const formData = await request.formData();
const file = formData.get('file');
const category = (formData.get('category') as string) || 'sonstiges';
const caseId = formData.get('caseId') as string | null;
const decisionId = formData.get('decisionId') as string | null;
const normInstrumentId = formData.get('normInstrumentId') as string | null;
const caseId = emptyToUndefined(formData.get('caseId') as string | null);
const decisionId = emptyToUndefined(formData.get('decisionId') as string | null);
const normInstrumentId = emptyToUndefined(formData.get('normInstrumentId') as string | null);
if (!file || !(file instanceof File)) {
return Response.json(
@@ -44,9 +50,9 @@ export async function POST(request: NextRequest) {
userId: ctx.userId,
file,
category: category as 'entscheidung' | 'norm' | 'falldokument' | 'sonstiges',
caseId: caseId ?? undefined,
decisionId: decisionId ?? undefined,
normInstrumentId: normInstrumentId ?? undefined,
caseId,
decisionId,
normInstrumentId,
});
await logAuditEvent(
@@ -75,9 +81,9 @@ export async function GET(request: NextRequest) {
const limit = Math.min(parseInt(searchParams.get('limit') ?? '20', 10), 100);
const offset = parseInt(searchParams.get('offset') ?? '0', 10);
const category = searchParams.get('category') as string | null;
const caseId = searchParams.get('caseId') as string | null;
const decisionId = searchParams.get('decisionId') as string | null;
const normInstrumentId = searchParams.get('normInstrumentId') as string | null;
const caseId = emptyToUndefined(searchParams.get('caseId'));
const decisionId = emptyToUndefined(searchParams.get('decisionId'));
const normInstrumentId = emptyToUndefined(searchParams.get('normInstrumentId'));
const docs = await listDocuments(
ctx.tenantId,
@@ -85,9 +91,9 @@ export async function GET(request: NextRequest) {
category: category && VALID_CATEGORIES.has(category)
? category as 'entscheidung' | 'norm' | 'falldokument' | 'sonstiges'
: undefined,
caseId: caseId ?? undefined,
decisionId: decisionId ?? undefined,
normInstrumentId: normInstrumentId ?? undefined,
caseId,
decisionId,
normInstrumentId,
},
limit,
offset,