From a7e846d733a05b9de6c4de79951ef5823ba447a2 Mon Sep 17 00:00:00 2001 From: gaoziman <2942894660@qq.com> Date: Fri, 19 Dec 2025 22:36:56 +0800 Subject: [PATCH] =?UTF-8?q?feat(API):=20=E5=AF=B9=E8=AF=9D=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E6=B7=BB=E5=8A=A0=E7=94=A8=E6=88=B7=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E9=9A=94=E7=A6=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 获取对话列表时按用户过滤 - 创建对话时关联当前用户 - 删除对话时验证所有权 - 所有对话操作需要登录认证 --- src/app/api/conversations/[id]/route.ts | 82 +++++++++++++++++++------ src/app/api/conversations/all/route.ts | 73 +++++++++++++++++----- src/app/api/conversations/route.ts | 56 +++++++++++++++-- 3 files changed, 172 insertions(+), 39 deletions(-) diff --git a/src/app/api/conversations/[id]/route.ts b/src/app/api/conversations/[id]/route.ts index 01af93b..1d06050 100644 --- a/src/app/api/conversations/[id]/route.ts +++ b/src/app/api/conversations/[id]/route.ts @@ -1,7 +1,8 @@ import { NextResponse } from 'next/server'; import { db } from '@/drizzle/db'; import { conversations, messages } from '@/drizzle/schema'; -import { eq, asc } from 'drizzle-orm'; +import { eq, asc, and } from 'drizzle-orm'; +import { getCurrentUser } from '@/lib/auth'; interface RouteParams { params: Promise<{ id: string }>; @@ -10,10 +11,22 @@ interface RouteParams { // GET /api/conversations/[id] - 获取单个对话及其消息 export async function GET(request: Request, { params }: RouteParams) { try { + // 获取当前用户 + const user = await getCurrentUser(); + if (!user) { + return NextResponse.json( + { error: '未登录' }, + { status: 401 } + ); + } + const { id } = await params; const conversation = await db.query.conversations.findFirst({ - where: eq(conversations.conversationId, id), + where: and( + eq(conversations.conversationId, id), + eq(conversations.userId, user.userId) + ), }); if (!conversation) { @@ -44,10 +57,34 @@ export async function GET(request: Request, { params }: RouteParams) { // PUT /api/conversations/[id] - 更新对话 export async function PUT(request: Request, { params }: RouteParams) { try { + // 获取当前用户 + const user = await getCurrentUser(); + if (!user) { + return NextResponse.json( + { error: '未登录' }, + { status: 401 } + ); + } + const { id } = await params; const body = await request.json(); const { title, isPinned, isArchived } = body; + // 验证对话属于当前用户 + const existingConversation = await db.query.conversations.findFirst({ + where: and( + eq(conversations.conversationId, id), + eq(conversations.userId, user.userId) + ), + }); + + if (!existingConversation) { + return NextResponse.json( + { error: 'Conversation not found' }, + { status: 404 } + ); + } + const updateData: Record = { updatedAt: new Date(), }; @@ -70,13 +107,6 @@ export async function PUT(request: Request, { params }: RouteParams) { .where(eq(conversations.conversationId, id)) .returning(); - if (!updated) { - return NextResponse.json( - { error: 'Conversation not found' }, - { status: 404 } - ); - } - return NextResponse.json(updated); } catch (error) { console.error('Failed to update conversation:', error); @@ -90,24 +120,40 @@ export async function PUT(request: Request, { params }: RouteParams) { // DELETE /api/conversations/[id] - 删除对话 export async function DELETE(request: Request, { params }: RouteParams) { try { + // 获取当前用户 + const user = await getCurrentUser(); + if (!user) { + return NextResponse.json( + { error: '未登录' }, + { status: 401 } + ); + } + const { id } = await params; - // 先删除相关消息 - await db.delete(messages).where(eq(messages.conversationId, id)); + // 验证对话属于当前用户 + const existingConversation = await db.query.conversations.findFirst({ + where: and( + eq(conversations.conversationId, id), + eq(conversations.userId, user.userId) + ), + }); - // 再删除对话 - const [deleted] = await db - .delete(conversations) - .where(eq(conversations.conversationId, id)) - .returning(); - - if (!deleted) { + if (!existingConversation) { return NextResponse.json( { error: 'Conversation not found' }, { status: 404 } ); } + // 先删除相关消息 + await db.delete(messages).where(eq(messages.conversationId, id)); + + // 再删除对话 + await db + .delete(conversations) + .where(eq(conversations.conversationId, id)); + return NextResponse.json({ success: true }); } catch (error) { console.error('Failed to delete conversation:', error); diff --git a/src/app/api/conversations/all/route.ts b/src/app/api/conversations/all/route.ts index 95542a1..c20c562 100644 --- a/src/app/api/conversations/all/route.ts +++ b/src/app/api/conversations/all/route.ts @@ -1,24 +1,48 @@ import { NextResponse } from 'next/server'; import { db } from '@/drizzle/db'; import { conversations, messages } from '@/drizzle/schema'; -import { sql } from 'drizzle-orm'; +import { sql, eq, inArray } from 'drizzle-orm'; +import { getCurrentUser } from '@/lib/auth'; -// GET /api/conversations/all - 获取统计信息 +// GET /api/conversations/all - 获取当前用户的统计信息 export async function GET() { try { - // 获取对话数量 + // 获取当前用户 + const user = await getCurrentUser(); + if (!user) { + return NextResponse.json( + { error: '未登录' }, + { status: 401 } + ); + } + + // 获取当前用户的对话数量 const conversationCount = await db .select({ count: sql`count(*)` }) - .from(conversations); + .from(conversations) + .where(eq(conversations.userId, user.userId)); - // 获取消息数量 - const messageCount = await db - .select({ count: sql`count(*)` }) - .from(messages); + // 获取当前用户的对话 ID 列表 + const userConversations = await db + .select({ conversationId: conversations.conversationId }) + .from(conversations) + .where(eq(conversations.userId, user.userId)); + + const conversationIds = userConversations.map(c => c.conversationId); + + // 获取当前用户的消息数量 + let messageCountValue = 0; + if (conversationIds.length > 0) { + const messageCount = await db + .select({ count: sql`count(*)` }) + .from(messages) + .where(inArray(messages.conversationId, conversationIds)); + messageCountValue = Number(messageCount[0]?.count || 0); + } return NextResponse.json({ conversationCount: Number(conversationCount[0]?.count || 0), - messageCount: Number(messageCount[0]?.count || 0), + messageCount: messageCountValue, }); } catch (error) { console.error('Failed to get stats:', error); @@ -29,18 +53,37 @@ export async function GET() { } } -// DELETE /api/conversations/all - 清除所有对话和消息 +// DELETE /api/conversations/all - 清除当前用户的所有对话和消息 export async function DELETE() { try { - // 先删除所有消息 - await db.delete(messages); + // 获取当前用户 + const user = await getCurrentUser(); + if (!user) { + return NextResponse.json( + { error: '未登录' }, + { status: 401 } + ); + } - // 再删除所有对话 - await db.delete(conversations); + // 获取当前用户的所有对话 ID + const userConversations = await db + .select({ conversationId: conversations.conversationId }) + .from(conversations) + .where(eq(conversations.userId, user.userId)); + + const conversationIds = userConversations.map(c => c.conversationId); + + if (conversationIds.length > 0) { + // 删除当前用户对话相关的所有消息 + await db.delete(messages).where(inArray(messages.conversationId, conversationIds)); + + // 删除当前用户的所有对话 + await db.delete(conversations).where(eq(conversations.userId, user.userId)); + } return NextResponse.json({ success: true, - message: 'All conversations and messages have been deleted', + message: 'All your conversations and messages have been deleted', }); } catch (error) { console.error('Failed to delete all conversations:', error); diff --git a/src/app/api/conversations/route.ts b/src/app/api/conversations/route.ts index c56523a..acd1030 100644 --- a/src/app/api/conversations/route.ts +++ b/src/app/api/conversations/route.ts @@ -1,14 +1,27 @@ import { NextResponse } from 'next/server'; import { db } from '@/drizzle/db'; import { conversations, messages } from '@/drizzle/schema'; -import { desc, eq } from 'drizzle-orm'; +import { desc, eq, and } from 'drizzle-orm'; import { nanoid } from 'nanoid'; +import { getCurrentUser } from '@/lib/auth'; -// GET /api/conversations - 获取对话列表 +// GET /api/conversations - 获取当前用户的对话列表 export async function GET() { try { + // 获取当前用户 + const user = await getCurrentUser(); + if (!user) { + return NextResponse.json( + { error: '未登录' }, + { status: 401 } + ); + } + const conversationList = await db.query.conversations.findMany({ - where: eq(conversations.isArchived, false), + where: and( + eq(conversations.isArchived, false), + eq(conversations.userId, user.userId) + ), orderBy: [desc(conversations.lastMessageAt)], }); @@ -25,6 +38,15 @@ export async function GET() { // POST /api/conversations - 创建新对话 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, model, tools, enableThinking } = body; @@ -38,6 +60,7 @@ export async function POST(request: Request) { model: model || 'claude-sonnet-4-20250514', tools: tools || [], enableThinking: enableThinking || false, + userId: user.userId, // 关联当前用户 }) .returning(); @@ -54,6 +77,15 @@ export async function POST(request: Request) { // DELETE /api/conversations - 批量删除对话 export async function DELETE(request: Request) { try { + // 获取当前用户 + const user = await getCurrentUser(); + if (!user) { + return NextResponse.json( + { error: '未登录' }, + { status: 401 } + ); + } + const body = await request.json(); const { conversationIds } = body; @@ -64,10 +96,22 @@ export async function DELETE(request: Request) { ); } - // 删除相关消息 + // 删除相关消息和对话(只删除属于当前用户的) for (const id of conversationIds) { - await db.delete(messages).where(eq(messages.conversationId, id)); - await db.delete(conversations).where(eq(conversations.conversationId, id)); + // 先验证对话属于当前用户 + const [conv] = await db + .select() + .from(conversations) + .where(and( + eq(conversations.conversationId, id), + eq(conversations.userId, user.userId) + )) + .limit(1); + + if (conv) { + await db.delete(messages).where(eq(messages.conversationId, id)); + await db.delete(conversations).where(eq(conversations.conversationId, id)); + } } return NextResponse.json({ success: true });