import { NextResponse } from "next/server"; import { chatSessionCreateInsertRow, chatSessionCreateSchema, ChatSessionBodyTooLargeError, readChatSessionJson, } from "@/lib/chat-session-write-contract"; import { consumeUserRequestRateLimit } from "@/lib/request-rate-limit"; import { resolveInheritedContextSummary } from "@/lib/session-context-summary"; import { isSupabaseConfigurationError } from "@/lib/supabase/config"; import { createServerSupabaseClient } from "@/lib/supabase/server"; import { clampSessionLimit, nextSessionCursor, parseSessionCursor, sessionCursorFilter, } from "@/lib/session-cursor"; import { excludeEmptyConsultations } from "@/lib/session-list-filter"; const SESSION_LIST_COLUMNS = "id,title,theme,model_id,session_type,rectification_case_id,chart_profile_id,chart_profile_name,chart_profile_role,updated_at,pinned,archived_at"; export async function GET(request: Request) { try { const url = new URL(request.url); const limit = clampSessionLimit(url.searchParams.get("limit")); const beforeRaw = url.searchParams.get("before"); const cursor = parseSessionCursor(beforeRaw); if (beforeRaw && !cursor) { return NextResponse.json({ error: "聊天记录请求无效" }, { status: 400 }); } const supabase = await createServerSupabaseClient(); const { data: { user }, error: authError } = await supabase.auth.getUser(); if (authError || !user) return NextResponse.json({ error: "请先登录" }, { status: 401 }); let pageQuery = excludeEmptyConsultations( supabase .from("chat_sessions") .select(SESSION_LIST_COLUMNS) .eq("user_id", user.id) .eq("pinned", false) .is("archived_at", null), ) .order("updated_at", { ascending: false }) .order("id", { ascending: false }) .limit(limit + 1); if (cursor) { pageQuery = pageQuery.or(sessionCursorFilter(cursor)); } const { data: pageRows, error: pageError } = await pageQuery; if (pageError) return NextResponse.json({ error: "聊天记录暂时无法读取" }, { status: 500 }); let pinnedRows: typeof pageRows = []; if (!cursor) { const { data: pinnedData, error: pinnedError } = await excludeEmptyConsultations( supabase .from("chat_sessions") .select(SESSION_LIST_COLUMNS) .eq("user_id", user.id) .eq("pinned", true) .is("archived_at", null), ) .order("updated_at", { ascending: false }) .order("id", { ascending: false }); if (pinnedError) return NextResponse.json({ error: "聊天记录暂时无法读取" }, { status: 500 }); pinnedRows = pinnedData ?? []; } const nextCursor = nextSessionCursor(pageRows ?? [], limit); const page = (pageRows ?? []).slice(0, limit); const sessions = cursor ? page : [...(pinnedRows ?? []), ...page]; return NextResponse.json({ sessions, nextCursor }); } catch (error) { if (isSupabaseConfigurationError(error)) { return NextResponse.json({ error: "数据库尚未配置", code: "DATABASE_NOT_CONFIGURED" }, { status: 503 }); } return NextResponse.json({ error: "聊天记录暂时无法读取" }, { status: 500 }); } } export async function POST(request: Request) { try { const supabase = await createServerSupabaseClient(); const { data: { user }, error: authError } = await supabase.auth.getUser(); if (authError || !user) return NextResponse.json({ error: "请先登录" }, { status: 401 }); const limited = consumeUserRequestRateLimit("sessionWrite", user.id); if (!limited.ok) { return NextResponse.json({ error: "请求过于频繁", code: "rate_limited", retryAfterSeconds: limited.retryAfterSeconds, }, { status: 429, headers: { "Retry-After": String(limited.retryAfterSeconds) } }); } const parsed = chatSessionCreateSchema.safeParse(await readChatSessionJson(request)); if (!parsed.success) return NextResponse.json({ error: "聊天记录格式不正确" }, { status: 400 }); const { id, updated_at: _ignoredClientClock, continued_from_session_id: continuedFromSessionId, ...values } = parsed.data; const inheritedSummary = await resolveInheritedContextSummary({ continuedFromSessionId, loadOwnedSummary: async (sourceId) => { const { data } = await supabase .from("chat_sessions") .select("context_summary") .eq("id", sourceId) .eq("user_id", user.id) .maybeSingle(); return data?.context_summary ?? null; }, }); const { error } = await supabase.from("chat_sessions").insert( chatSessionCreateInsertRow({ id, userId: user.id, values, inheritedSummary, updatedAt: new Date().toISOString(), }), ); if (error) return NextResponse.json({ error: "聊天记录暂时无法同步" }, { status: 500 }); return NextResponse.json({ ok: true }, { status: 201 }); } catch (error) { if (error instanceof ChatSessionBodyTooLargeError) { return NextResponse.json({ error: error.message }, { status: 413 }); } if (isSupabaseConfigurationError(error)) { return NextResponse.json({ error: "数据库尚未配置", code: "DATABASE_NOT_CONFIGURED" }, { status: 503 }); } return NextResponse.json({ error: "聊天记录暂时无法同步" }, { status: 500 }); } }