Livre US-FLASHCARDS avec decks, session de révision, stats et migration Prisma. Finalise le Web Clipper (i18n 15 langues) et corrige les erreurs ESLint bloquant la CI. Co-authored-by: Cursor <cursoragent@cursor.com>
85 lines
2.6 KiB
TypeScript
85 lines
2.6 KiB
TypeScript
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<string>()
|
|
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) {
|
|
const 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 })
|
|
}
|