diff --git a/app/(chat)/api/chat/route.ts b/app/(chat)/api/chat/route.ts index ac52197803..30e0b0febc 100644 --- a/app/(chat)/api/chat/route.ts +++ b/app/(chat)/api/chat/route.ts @@ -32,6 +32,7 @@ import { getChatById, getMessageCountByUserId, getMessagesByChatId, + getStreamIdsByChatId, saveChat, saveMessages, updateChatTitleById, @@ -57,6 +58,63 @@ function getStreamContext() { export { getStreamContext }; +export async function GET(request: Request) { + const { searchParams } = new URL(request.url); + const chatId = searchParams.get("chatId"); + + if (!chatId) { + return new ChatbotError("bad_request:api").toResponse(); + } + + const session = await auth(); + + if (!session?.user) { + return new ChatbotError("unauthorized:chat").toResponse(); + } + + const chat = await getChatById({ id: chatId }); + + if (!chat) { + return new Response(null, { status: 204 }); + } + + if (chat.userId !== session.user.id) { + return new ChatbotError("forbidden:chat").toResponse(); + } + + const streamContext = getStreamContext(); + + if (!streamContext) { + return new Response(null, { status: 204 }); + } + + const streamIds = await getStreamIdsByChatId({ chatId }); + + if (!streamIds.length) { + return new Response(null, { status: 204 }); + } + + const recentStreamId = streamIds.at(-1); + + if (!recentStreamId) { + return new Response(null, { status: 204 }); + } + + const emptyStream = new ReadableStream({ + start(controller) { + controller.close(); + }, + }); + + return new Response( + await streamContext.resumeExistingStream(recentStreamId, emptyStream), + { + status: 200, + headers: { "Content-Type": "text/event-stream" }, + } + ); +} + export async function POST(request: Request) { let requestBody: PostRequestBody; diff --git a/tests/e2e/api.test.ts b/tests/e2e/api.test.ts index 9b787796b7..a5a49e380f 100644 --- a/tests/e2e/api.test.ts +++ b/tests/e2e/api.test.ts @@ -76,6 +76,22 @@ test.describe("Chat Error Handling", () => { }); }); +test.describe("Resumable Streams", () => { + test("GET /api/chat returns 400 when chatId is missing", async ({ + request, + }) => { + const response = await request.get("/api/chat"); + expect(response.status()).toBe(400); + }); + + test("GET /api/chat returns 401 when not authenticated", async ({ + request, + }) => { + const response = await request.get("/api/chat?chatId=nonexistent"); + expect(response.status()).toBe(401); + }); +}); + test.describe("Suggested Actions", () => { test("suggested actions are clickable", async ({ page }) => { await page.goto("/");