import { NextRequest, NextResponse } from 'next/server'; import { prisma } from '@/lib/prisma'; import { requireApiAuth } from '@/lib/api-auth'; import { cleanupAccidentalStandaloneInlineCode, markdownToImportHtml } from '@/lib/markdown-import'; import { MAX_TITLE_LENGTH, safeParseTags, normalizeTags } from '@/lib/page-utils'; export async function GET(request: NextRequest) { const authError = await requireApiAuth(); if (authError) return authError; try { const searchParams = request.nextUrl.searchParams; // lightweight 模式:不返回 content,用于侧边栏等场景 const lightweight = searchParams.get('lightweight') === 'true'; const page = parseInt(searchParams.get('page') || '0', 10); const limit = parseInt(searchParams.get('limit') || '0', 10); // 默认行为:返回所有页面(向后兼容) const usePagination = limit > 0 && limit <= 1000 && page > 0; if (!usePagination) { const pages = await prisma.page.findMany({ orderBy: [{ order: 'asc' }, { createdAt: 'desc' }], }); const parsedPages = pages.map((p) => ({ ...p, content: lightweight ? '' : p.content, tags: safeParseTags(p.tags), })); return NextResponse.json(parsedPages); } // 分页查询 const skip = (page - 1) * limit; const [pages, total] = await Promise.all([ prisma.page.findMany({ skip, take: limit, orderBy: [{ order: 'asc' }, { createdAt: 'desc' }], }), prisma.page.count(), ]); const parsedPages = pages.map((p) => ({ ...p, content: lightweight ? '' : p.content, tags: safeParseTags(p.tags), })); return NextResponse.json({ data: parsedPages, pagination: { page, limit, total, totalPages: Math.ceil(total / limit), }, }); } catch { return NextResponse.json({ error: 'Error fetching pages' }, { status: 500 }); } } export async function POST(request: Request) { const authError = await requireApiAuth(); if (authError) return authError; try { const body = await request.json().catch(() => null); if (!body || typeof body !== 'object') { return NextResponse.json({ error: 'Invalid request body' }, { status: 400 }); } const rawTitle = typeof body.title === 'string' ? body.title.trim() : ''; const title = rawTitle || '无标题'; if (title.length > MAX_TITLE_LENGTH) { return NextResponse.json({ error: 'Title too long' }, { status: 400 }); } const type = body.type === 'folder' ? 'folder' : 'file'; const importMarkdown = typeof body.importMarkdown === 'string' ? body.importMarkdown : null; const rawContent = typeof body.content === 'string' ? body.content : ''; const content = cleanupAccidentalStandaloneInlineCode( importMarkdown !== null ? markdownToImportHtml(importMarkdown) : rawContent ); if (importMarkdown !== null && process.env.NODE_ENV !== 'production') { const lines = importMarkdown.replace(/\r\n?/g, '\n').split('\n'); const fenceLines = lines .map((line: string, idx: number) => (line.trimStart().startsWith('```') || line.trimStart().startsWith('~~~') ? idx + 1 : -1)) .filter((n: number) => n > 0); const preEnd = content.indexOf(''); console.info(`[import-md][POST] title="${title}" fences=${fenceLines.join(',') || 'none'} preEnd=${preEnd} len=${content.length}`); } const parentId = typeof body.parentId === 'string' ? body.parentId : null; const tags = normalizeTags(body.tags); const icon = typeof body.icon === 'string' ? body.icon : null; const isLocked = typeof body.isLocked === 'boolean' ? body.isLocked : false; const requestedOrder = Number.isInteger(body.order) ? body.order : undefined; if (parentId) { const parent = await prisma.page.findUnique({ where: { id: parentId }, select: { id: true, type: true }, }); if (!parent || parent.type !== 'folder') { return NextResponse.json({ error: 'Parent folder not found' }, { status: 400 }); } } const page = await prisma.page.create({ data: { title, content, tags: JSON.stringify(tags), parentId, type, order: await (async () => { if (requestedOrder !== undefined) return requestedOrder; const lastPage = await prisma.page.findFirst({ where: { parentId }, orderBy: { order: 'desc' }, }); return (lastPage?.order ?? -1) + 1; })(), icon, isLocked, }, }); return NextResponse.json({ ...page, tags: safeParseTags(page.tags) }); } catch { return NextResponse.json({ error: 'Error creating page' }, { status: 500 }); } }