/ src / app / api / approvals / route.ts
route.ts
 1  import { NextResponse } from 'next/server'
 2  import { z } from 'zod'
 3  
 4  import { listPendingApprovals, submitDecision } from '@/lib/server/approvals'
 5  import { safeParseBody } from '@/lib/server/safe-parse-body'
 6  import { loadApprovals } from '@/lib/server/storage'
 7  import { errorMessage } from '@/lib/shared-utils'
 8  import type { ApprovalCategory } from '@/types'
 9  
10  export const dynamic = 'force-dynamic'
11  
12  const ALLOWED_CATEGORIES: ApprovalCategory[] = [
13    'human_loop', 'tool_access', 'extension_scaffold', 'extension_install',
14    'task_tool', 'connector_sender', 'agent_create', 'budget_change', 'delegation_enable',
15  ]
16  
17  const ApprovalDecisionSchema = z.object({
18    id: z.string().min(1, 'id is required'),
19    approved: z.boolean(),
20  })
21  
22  export async function GET(req: Request) {
23    const { searchParams } = new URL(req.url)
24    const categoryParam = searchParams.get('category') as ApprovalCategory | null
25    const category = categoryParam && ALLOWED_CATEGORIES.includes(categoryParam)
26      ? categoryParam
27      : undefined
28    return NextResponse.json(listPendingApprovals(category))
29  }
30  
31  export async function POST(req: Request) {
32    const { data: body, error } = await safeParseBody(req, ApprovalDecisionSchema)
33    if (error) return error
34  
35    try {
36      const approval = loadApprovals()[body.id]
37      if (!approval) {
38        return NextResponse.json({ error: 'approval not found' }, { status: 404 })
39      }
40      await submitDecision(body.id, body.approved)
41      return NextResponse.json({ ok: true })
42    } catch (err: unknown) {
43      return NextResponse.json({ error: errorMessage(err) }, { status: 500 })
44    }
45  }