Scrum4Me/app/api/user-questions/[id]/answer/route.ts
Scrum4Me Agent 226bd05594 feat(ST-imalmyr7): POST /api/user-questions/[id]/answer — worker-antwoord endpoint
- Bearer-auth via authenticateApiRequest (token_hash SHA-256)
- Zod-validatie van body.answer (max 8000 chars)
- Guard: 404 bij onbekend id, 409 als al beantwoord
- Slaat answer op en zet status naar answered
- pg_notify scrum4me_changes met user_question entity-event

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 17:32:41 +02:00

62 lines
1.6 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { authenticateApiRequest } from '@/lib/api-auth'
import { prisma } from '@/lib/prisma'
interface RouteContext {
params: Promise<{ id: string }>
}
const bodySchema = z.object({
answer: z.string().min(1).max(8000),
})
export async function POST(request: NextRequest, ctx: RouteContext) {
const auth = await authenticateApiRequest(request)
if ('error' in auth) {
return NextResponse.json({ error: auth.error }, { status: auth.status })
}
const { id } = await ctx.params
let body: unknown
try {
body = await request.json()
} catch {
return NextResponse.json({ error: 'Malformed JSON' }, { status: 400 })
}
const parsed = bodySchema.safeParse(body)
if (!parsed.success) {
return NextResponse.json({ error: 'Ongeldige invoer', details: parsed.error.flatten() }, { status: 422 })
}
const uq = await prisma.userQuestion.findFirst({
where: { id },
select: { id: true, idea_id: true, status: true },
})
if (!uq) {
return NextResponse.json({ error: 'UserQuestion niet gevonden' }, { status: 404 })
}
if (uq.status !== 'pending') {
return NextResponse.json({ error: 'Vraag is al beantwoord' }, { status: 409 })
}
await prisma.userQuestion.update({
where: { id },
data: { answer: parsed.data.answer, status: 'answered' },
})
await prisma.$executeRaw`
SELECT pg_notify('scrum4me_changes', ${JSON.stringify({
op: 'U',
entity: 'user_question',
id,
idea_id: uq.idea_id,
status: 'answered',
})}::text)
`
return NextResponse.json({ ok: true })
}