import { NextRequest } from 'next/server' import { prisma } from '@/lib/prisma' import { noteSchema, NoteInput } from '@/lib/validators' import { createErrorResponse, createSuccessResponse, ValidationError } from '@/lib/errors' import { syncBacklinks } from '@/lib/backlinks' import { createBackupSnapshot } from '@/lib/backup' import { notesToMarkdownZip, noteToMarkdown } from '@/lib/export-markdown' import { notesToHtmlZip, noteToHtml } from '@/lib/export-html' export async function GET(req: NextRequest) { try { const { searchParams } = new URL(req.url) const format = searchParams.get('format') if (format === 'backup') { const backup = await createBackupSnapshot('manual') return createSuccessResponse(backup) } const notes = await prisma.note.findMany({ include: { tags: { include: { tag: true } } }, }) const exportData = notes.map(note => ({ ...note, tags: note.tags.map(nt => nt.tag.name), createdAt: note.createdAt.toISOString(), updatedAt: note.updatedAt.toISOString(), })) if (format === 'markdown') { const notesForExport = notes.map(note => ({ ...note, tags: note.tags, createdAt: note.createdAt.toISOString(), updatedAt: note.updatedAt.toISOString(), })) if (notes.length === 1) { return createSuccessResponse({ filename: notesToMarkdownZip(notesForExport).files[0].name, content: noteToMarkdown(notesForExport[0]), }) } return createSuccessResponse(notesToMarkdownZip(notesForExport)) } if (format === 'html') { const notesForExport = notes.map(note => ({ ...note, tags: note.tags, createdAt: note.createdAt.toISOString(), updatedAt: note.updatedAt.toISOString(), })) if (notes.length === 1) { return createSuccessResponse({ filename: notesToHtmlZip(notesForExport).files[0].name, content: noteToHtml(notesForExport[0]), }) } return createSuccessResponse(notesToHtmlZip(notesForExport)) } return createSuccessResponse(exportData) } catch (error) { return createErrorResponse(error) } } export async function POST(req: NextRequest) { try { const body = await req.json() if (!Array.isArray(body)) { throw new ValidationError([{ path: 'body', message: 'Invalid format: expected array' }]) } const importedNotes: NoteInput[] = [] const errors: string[] = [] for (let i = 0; i < body.length; i++) { const result = noteSchema.safeParse(body[i]) if (!result.success) { errors.push(`Item ${i}: ${result.error.issues.map(e => e.message).join(', ')}`) continue } importedNotes.push(result.data) } if (errors.length > 0) { throw new ValidationError(errors) } const parseDate = (dateStr: string | undefined): Date => { if (!dateStr) return new Date() const parsed = new Date(dateStr) return isNaN(parsed.getTime()) ? new Date() : parsed } let processed = 0 await prisma.$transaction(async (tx) => { for (const item of importedNotes) { const tags = item.tags || [] const { tags: _, ...noteData } = item const createdAt = parseDate((item as { createdAt?: string }).createdAt) const updatedAt = parseDate((item as { updatedAt?: string }).updatedAt) const itemWithId = item as { id?: string } if (itemWithId.id) { const existing = await tx.note.findUnique({ where: { id: itemWithId.id } }) if (existing) { await tx.note.update({ where: { id: itemWithId.id }, data: { ...noteData, createdAt, updatedAt }, }) await tx.noteTag.deleteMany({ where: { noteId: itemWithId.id } }) processed++ } else { await tx.note.create({ data: { ...noteData, id: itemWithId.id, createdAt, updatedAt, creationSource: 'import', }, }) processed++ } } else { const existingByTitle = await tx.note.findFirst({ where: { title: item.title }, }) if (existingByTitle) { await tx.note.update({ where: { id: existingByTitle.id }, data: { ...noteData, updatedAt }, }) await tx.noteTag.deleteMany({ where: { noteId: existingByTitle.id } }) } else { await tx.note.create({ data: { ...noteData, createdAt, updatedAt, creationSource: 'import', }, }) } processed++ } const noteId = itemWithId.id ? (await tx.note.findUnique({ where: { id: itemWithId.id } }))?.id : (await tx.note.findFirst({ where: { title: item.title } }))?.id if (noteId && tags.length > 0) { for (const tagName of tags) { const tag = await tx.tag.upsert({ where: { name: tagName }, create: { name: tagName }, update: {}, }) await tx.noteTag.create({ data: { noteId, tagId: tag.id }, }) } } if (noteId) { const note = await tx.note.findUnique({ where: { id: noteId } }) if (note) { await syncBacklinks(note.id, note.content) } } } }) return createSuccessResponse({ success: true, count: processed }, 201) } catch (error) { return createErrorResponse(error) } }