import { NextResponse } from 'next/server' import prisma from '@/lib/prisma' import { auth } from '@/auth' const WIKILINK_RE = /\[\[([^\]|#]+?)(?:[|#][^\]]+)?\]\]/g function extractWikilinks(content: string): { title: string; snippet: string }[] { const plain = content.replace(/<[^>]+>/g, ' ') const results: { title: string; snippet: string }[] = [] const seen = new Set() let match: RegExpExecArray | null WIKILINK_RE.lastIndex = 0 while ((match = WIKILINK_RE.exec(plain)) !== null) { const title = match[1].trim() if (!title || seen.has(title.toLowerCase())) continue seen.add(title.toLowerCase()) const start = Math.max(0, match.index - 50) const end = Math.min(plain.length, match.index + match[0].length + 50) const snippet = plain.slice(start, end).replace(/\s+/g, ' ').trim() results.push({ title, snippet }) } return results } /** * POST /api/graph/sync-all * Batch-sync [[wikilinks]] for ALL notes of the authenticated user. * Call once to populate the NoteLink table from existing notes. */ export async function POST() { const session = await auth() if (!session?.user?.id) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) } const userId = session.user.id // Get all non-trashed notes with content const notes = await prisma.note.findMany({ where: { userId, trashedAt: null, content: { not: '' } }, select: { id: true, content: true, notebookId: true }, }) let totalLinks = 0 for (const note of notes) { if (!note.content.includes('[[')) continue const wikilinks = extractWikilinks(note.content) if (wikilinks.length === 0) continue for (const { title, snippet } of wikilinks) { let targetNote = await prisma.note.findFirst({ where: { userId, title: { equals: title, mode: 'insensitive' }, trashedAt: null, }, select: { id: true }, }) if (!targetNote) { // Skip stubs in batch sync — we only link existing notes continue } if (targetNote.id === note.id) continue try { await (prisma as any).noteLink.upsert({ where: { sourceNoteId_targetNoteId: { sourceNoteId: note.id, targetNoteId: targetNote.id } }, update: { contextSnippet: snippet }, create: { sourceNoteId: note.id, targetNoteId: targetNote.id, contextSnippet: snippet }, }) totalLinks++ } catch { // ignore duplicate constraint errors } } } return NextResponse.json({ synced: notes.length, links: totalLinks }) }