diff --git a/src/app/api/notes/[noteId]/route.ts b/src/app/api/notes/[noteId]/route.ts new file mode 100644 index 0000000..a702d20 --- /dev/null +++ b/src/app/api/notes/[noteId]/route.ts @@ -0,0 +1,160 @@ +import { NextResponse } from 'next/server'; +import { db } from '@/drizzle/db'; +import { notes } from '@/drizzle/schema'; +import { eq, and } from 'drizzle-orm'; +import { getCurrentUser } from '@/lib/auth'; + +// GET /api/notes/[noteId] - 获取单个笔记 +export async function GET( + request: Request, + { params }: { params: Promise<{ noteId: string }> } +) { + try { + const { noteId } = await params; + + // 获取当前用户 + const user = await getCurrentUser(); + if (!user) { + return NextResponse.json( + { error: '未登录' }, + { status: 401 } + ); + } + + const note = await db.query.notes.findFirst({ + where: and( + eq(notes.noteId, noteId), + eq(notes.userId, user.userId) + ), + }); + + if (!note) { + return NextResponse.json( + { error: '笔记不存在' }, + { status: 404 } + ); + } + + return NextResponse.json({ note }); + } catch (error) { + console.error('Failed to get note:', error); + return NextResponse.json( + { error: '获取笔记失败' }, + { status: 500 } + ); + } +} + +// PUT /api/notes/[noteId] - 更新笔记 +export async function PUT( + request: Request, + { params }: { params: Promise<{ noteId: string }> } +) { + try { + const { noteId } = await params; + + // 获取当前用户 + const user = await getCurrentUser(); + if (!user) { + return NextResponse.json( + { error: '未登录' }, + { status: 401 } + ); + } + + // 验证笔记存在且属于当前用户 + const existingNote = await db.query.notes.findFirst({ + where: and( + eq(notes.noteId, noteId), + eq(notes.userId, user.userId) + ), + }); + + if (!existingNote) { + return NextResponse.json( + { error: '笔记不存在' }, + { status: 404 } + ); + } + + const body = await request.json(); + const { title, content, tags, isPinned, isArchived } = body; + + // 构建更新数据 + const updateData: Record = { + updatedAt: new Date(), + }; + + if (title !== undefined) updateData.title = title.trim(); + if (content !== undefined) updateData.content = content.trim(); + if (tags !== undefined) updateData.tags = tags; + if (isPinned !== undefined) updateData.isPinned = isPinned; + if (isArchived !== undefined) updateData.isArchived = isArchived; + + const [updatedNote] = await db + .update(notes) + .set(updateData) + .where(and( + eq(notes.noteId, noteId), + eq(notes.userId, user.userId) + )) + .returning(); + + return NextResponse.json({ note: updatedNote }); + } catch (error) { + console.error('Failed to update note:', error); + return NextResponse.json( + { error: '更新笔记失败' }, + { status: 500 } + ); + } +} + +// DELETE /api/notes/[noteId] - 删除笔记 +export async function DELETE( + request: Request, + { params }: { params: Promise<{ noteId: string }> } +) { + try { + const { noteId } = await params; + + // 获取当前用户 + const user = await getCurrentUser(); + if (!user) { + return NextResponse.json( + { error: '未登录' }, + { status: 401 } + ); + } + + // 验证笔记存在且属于当前用户 + const existingNote = await db.query.notes.findFirst({ + where: and( + eq(notes.noteId, noteId), + eq(notes.userId, user.userId) + ), + }); + + if (!existingNote) { + return NextResponse.json( + { error: '笔记不存在' }, + { status: 404 } + ); + } + + await db + .delete(notes) + .where(and( + eq(notes.noteId, noteId), + eq(notes.userId, user.userId) + )); + + return NextResponse.json({ success: true }); + } catch (error) { + console.error('Failed to delete note:', error); + return NextResponse.json( + { error: '删除笔记失败' }, + { status: 500 } + ); + } +} diff --git a/src/app/api/notes/route.ts b/src/app/api/notes/route.ts new file mode 100644 index 0000000..2416b74 --- /dev/null +++ b/src/app/api/notes/route.ts @@ -0,0 +1,122 @@ +import { NextResponse } from 'next/server'; +import { db } from '@/drizzle/db'; +import { notes } from '@/drizzle/schema'; +import { desc, eq, and, or, ilike, sql } from 'drizzle-orm'; +import { nanoid } from 'nanoid'; +import { getCurrentUser } from '@/lib/auth'; + +// GET /api/notes - 获取当前用户的笔记列表 +export async function GET(request: Request) { + try { + // 获取当前用户 + const user = await getCurrentUser(); + if (!user) { + return NextResponse.json( + { error: '未登录' }, + { status: 401 } + ); + } + + // 解析查询参数 + const { searchParams } = new URL(request.url); + const search = searchParams.get('search'); + const tags = searchParams.get('tags'); + const isPinned = searchParams.get('isPinned'); + const isArchived = searchParams.get('isArchived'); + + // 构建查询条件 + const conditions = [eq(notes.userId, user.userId)]; + + // 搜索条件(标题或内容) + if (search) { + conditions.push( + or( + ilike(notes.title, `%${search}%`), + ilike(notes.content, `%${search}%`) + )! + ); + } + + // 标签筛选 + if (tags) { + const tagList = tags.split(',').map(t => t.trim()); + // 使用 jsonb 包含查询 + conditions.push( + sql`${notes.tags} ?| array[${sql.join(tagList.map(t => sql`${t}`), sql`, `)}]` + ); + } + + // 置顶筛选 + if (isPinned !== null && isPinned !== undefined) { + conditions.push(eq(notes.isPinned, isPinned === 'true')); + } + + // 归档筛选(默认不显示已归档) + if (isArchived !== null && isArchived !== undefined) { + conditions.push(eq(notes.isArchived, isArchived === 'true')); + } else { + conditions.push(eq(notes.isArchived, false)); + } + + const noteList = await db.query.notes.findMany({ + where: and(...conditions), + orderBy: [desc(notes.isPinned), desc(notes.createdAt)], + }); + + return NextResponse.json({ notes: noteList }); + } catch (error) { + console.error('Failed to get notes:', error); + return NextResponse.json( + { error: '获取笔记列表失败' }, + { status: 500 } + ); + } +} + +// POST /api/notes - 创建新笔记 +export async function POST(request: Request) { + try { + // 获取当前用户 + const user = await getCurrentUser(); + if (!user) { + return NextResponse.json( + { error: '未登录' }, + { status: 401 } + ); + } + + const body = await request.json(); + const { title, content, tags, conversationId, messageId } = body; + + // 验证必填字段 + if (!title || !content) { + return NextResponse.json( + { error: '标题和内容不能为空' }, + { status: 400 } + ); + } + + const noteId = nanoid(); + + const [newNote] = await db + .insert(notes) + .values({ + noteId, + userId: user.userId, + title: title.trim(), + content: content.trim(), + tags: tags || [], + conversationId: conversationId || null, + messageId: messageId || null, + }) + .returning(); + + return NextResponse.json({ note: newNote }, { status: 201 }); + } catch (error) { + console.error('Failed to create note:', error); + return NextResponse.json( + { error: '创建笔记失败' }, + { status: 500 } + ); + } +}