spb/note-spboucher-ai
Public
1import { z } from "zod";2import { handler, parseBody, uuid } from "@/lib/api";3import { listSuggestions, suggestionCounts, decideSuggestion } from "@/lib/graph/service";4import { suggestLinksForPage } from "@/lib/graph/suggest";5import { enqueue, QUEUES } from "@/lib/jobs";6import { audit } from "@/lib/audit";78/** GET ?status=suggested|accepted|rejected&pageId= */9export const GET = handler(async ({ req, ws }) => {10 const u = new URL(req.url).searchParams;11 const status = (["suggested", "accepted", "rejected"] as const).find((s) => s === u.get("status")) ?? "suggested";12 const pid = uuid.safeParse(u.get("pageId"));13 const [items, counts] = await Promise.all([listSuggestions(ws.id, status, { pageId: pid.success ? pid.data : null }), suggestionCounts(ws.id)]);14 return Response.json({ items, counts }, { headers: { "cache-control": "private, no-store" } });15});1617/** POST { action: "suggest", pageId?, inline? } — enqueue the suggest-links job (inline=true runs one page synchronously and returns the result). */18export const POST = handler(async ({ req, ws, user }) => {19 const b = await parseBody(req, z.object({ action: z.literal("suggest"), pageId: uuid.nullable().optional(), inline: z.boolean().optional() }));20 if (b.inline && b.pageId) {21 const r = await suggestLinksForPage(ws.id, b.pageId, { k: 8 });22 await audit(user.id, "graph.suggest", { type: "page", id: b.pageId }, { inline: true, suggested: r.suggested, candidates: r.candidates });23 return Response.json({ queued: false, result: r });24 }25 const jobId = await enqueue(QUEUES.suggestLinks, { workspaceId: ws.id, pageId: b.pageId ?? null }, b.pageId ? { singletonKey: `suggest:${b.pageId}`, singletonSeconds: 30 } : { singletonKey: `suggest:ws:${ws.id}`, singletonSeconds: 300 });26 await audit(user.id, "graph.suggest", b.pageId ? { type: "page", id: b.pageId } : { type: "workspace", id: ws.id }, { jobId });27 return Response.json({ queued: true, jobId });28});2930/** PATCH { id, status: "accepted" | "rejected" } */31export const PATCH = handler(async ({ req, ws, user }) => {32 const b = await parseBody(req, z.object({ id: uuid, status: z.enum(["accepted", "rejected"]) }));33 const k = await decideSuggestion(ws.id, b.id, b.status);34 await audit(user.id, `graph.suggestion.${b.status}`, { type: "knowledge_edge", id: b.id }, { from: k.fromId, to: k.toId });35 return Response.json({ ok: true, id: k.id, status: k.status, fromId: k.fromId, toId: k.toId });36});37