Restore Ultracite + fix sidebar (#1233)

This commit is contained in:
Hayden Bleasel 2025-09-21 11:02:31 -07:00 committed by GitHub
parent 8fbfc253fa
commit 947ed094a6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
177 changed files with 6908 additions and 8306 deletions

View file

@ -1,18 +1,18 @@
'use server';
"use server";
import { generateText, type UIMessage } from 'ai';
import { cookies } from 'next/headers';
import { generateText, type UIMessage } from "ai";
import { cookies } from "next/headers";
import type { VisibilityType } from "@/components/visibility-selector";
import { myProvider } from "@/lib/ai/providers";
import {
deleteMessagesByChatIdAfterTimestamp,
getMessageById,
updateChatVisiblityById,
} from '@/lib/db/queries';
import type { VisibilityType } from '@/components/visibility-selector';
import { myProvider } from '@/lib/ai/providers';
} from "@/lib/db/queries";
export async function saveChatModelAsCookie(model: string) {
const cookieStore = await cookies();
cookieStore.set('chat-model', model);
cookieStore.set("chat-model", model);
}
export async function generateTitleFromUserMessage({
@ -21,7 +21,7 @@ export async function generateTitleFromUserMessage({
message: UIMessage;
}) {
const { text: title } = await generateText({
model: myProvider.languageModel('title-model'),
model: myProvider.languageModel("title-model"),
system: `\n
- you will generate a short title based on the first message a user begins a conversation with
- ensure it is not more than 80 characters long

View file

@ -1,19 +1,19 @@
import { auth } from '@/app/(auth)/auth';
import { createUIMessageStream, JsonToSseTransformStream } from "ai";
import { differenceInSeconds } from "date-fns";
import { auth } from "@/app/(auth)/auth";
import {
getChatById,
getMessagesByChatId,
getStreamIdsByChatId,
} from '@/lib/db/queries';
import type { Chat } from '@/lib/db/schema';
import { ChatSDKError } from '@/lib/errors';
import type { ChatMessage } from '@/lib/types';
import { createUIMessageStream, JsonToSseTransformStream } from 'ai';
import { getStreamContext } from '../../route';
import { differenceInSeconds } from 'date-fns';
} from "@/lib/db/queries";
import type { Chat } from "@/lib/db/schema";
import { ChatSDKError } from "@/lib/errors";
import type { ChatMessage } from "@/lib/types";
import { getStreamContext } from "../../route";
export async function GET(
_: Request,
{ params }: { params: Promise<{ id: string }> },
{ params }: { params: Promise<{ id: string }> }
) {
const { id: chatId } = await params;
@ -25,13 +25,13 @@ export async function GET(
}
if (!chatId) {
return new ChatSDKError('bad_request:api').toResponse();
return new ChatSDKError("bad_request:api").toResponse();
}
const session = await auth();
if (!session?.user) {
return new ChatSDKError('unauthorized:chat').toResponse();
return new ChatSDKError("unauthorized:chat").toResponse();
}
let chat: Chat | null;
@ -39,35 +39,36 @@ export async function GET(
try {
chat = await getChatById({ id: chatId });
} catch {
return new ChatSDKError('not_found:chat').toResponse();
return new ChatSDKError("not_found:chat").toResponse();
}
if (!chat) {
return new ChatSDKError('not_found:chat').toResponse();
return new ChatSDKError("not_found:chat").toResponse();
}
if (chat.visibility === 'private' && chat.userId !== session.user.id) {
return new ChatSDKError('forbidden:chat').toResponse();
if (chat.visibility === "private" && chat.userId !== session.user.id) {
return new ChatSDKError("forbidden:chat").toResponse();
}
const streamIds = await getStreamIdsByChatId({ chatId });
if (!streamIds.length) {
return new ChatSDKError('not_found:stream').toResponse();
return new ChatSDKError("not_found:stream").toResponse();
}
const recentStreamId = streamIds.at(-1);
if (!recentStreamId) {
return new ChatSDKError('not_found:stream').toResponse();
return new ChatSDKError("not_found:stream").toResponse();
}
const emptyDataStream = createUIMessageStream<ChatMessage>({
// biome-ignore lint/suspicious/noEmptyBlockStatements: "Needs to exist"
execute: () => {},
});
const stream = await streamContext.resumableStream(recentStreamId, () =>
emptyDataStream.pipeThrough(new JsonToSseTransformStream()),
emptyDataStream.pipeThrough(new JsonToSseTransformStream())
);
/*
@ -82,7 +83,7 @@ export async function GET(
return new Response(emptyDataStream, { status: 200 });
}
if (mostRecentMessage.role !== 'assistant') {
if (mostRecentMessage.role !== "assistant") {
return new Response(emptyDataStream, { status: 200 });
}
@ -95,7 +96,7 @@ export async function GET(
const restoredStream = createUIMessageStream<ChatMessage>({
execute: ({ writer }) => {
writer.write({
type: 'data-appendMessage',
type: "data-appendMessage",
data: JSON.stringify(mostRecentMessage),
transient: true,
});
@ -104,7 +105,7 @@ export async function GET(
return new Response(
restoredStream.pipeThrough(new JsonToSseTransformStream()),
{ status: 200 },
{ status: 200 }
);
}

View file

@ -1,3 +1,4 @@
import { geolocation } from "@vercel/functions";
import {
convertToModelMessages,
createUIMessageStream,
@ -5,9 +6,27 @@ import {
smoothStream,
stepCountIs,
streamText,
} from 'ai';
import { auth, type UserType } from '@/app/(auth)/auth';
import { type RequestHints, systemPrompt } from '@/lib/ai/prompts';
} from "ai";
import { unstable_cache as cache } from "next/cache";
import { after } from "next/server";
import {
createResumableStreamContext,
type ResumableStreamContext,
} from "resumable-stream";
import type { ModelCatalog } from "tokenlens/core";
import { fetchModels } from "tokenlens/fetch";
import { getUsage } from "tokenlens/helpers";
import { auth, type UserType } from "@/app/(auth)/auth";
import type { VisibilityType } from "@/components/visibility-selector";
import { entitlementsByUserType } from "@/lib/ai/entitlements";
import type { ChatModel } from "@/lib/ai/models";
import { type RequestHints, systemPrompt } from "@/lib/ai/prompts";
import { myProvider } from "@/lib/ai/providers";
import { createDocument } from "@/lib/ai/tools/create-document";
import { getWeather } from "@/lib/ai/tools/get-weather";
import { requestSuggestions } from "@/lib/ai/tools/request-suggestions";
import { updateDocument } from "@/lib/ai/tools/update-document";
import { isProductionEnvironment } from "@/lib/constants";
import {
createStreamId,
deleteChatById,
@ -16,33 +35,14 @@ import {
getMessagesByChatId,
saveChat,
saveMessages,
} from '@/lib/db/queries';
import { updateChatLastContextById } from '@/lib/db/queries';
import { convertToUIMessages, generateUUID } from '@/lib/utils';
import { generateTitleFromUserMessage } from '../../actions';
import { createDocument } from '@/lib/ai/tools/create-document';
import { updateDocument } from '@/lib/ai/tools/update-document';
import { requestSuggestions } from '@/lib/ai/tools/request-suggestions';
import { getWeather } from '@/lib/ai/tools/get-weather';
import { isProductionEnvironment } from '@/lib/constants';
import { myProvider } from '@/lib/ai/providers';
import { entitlementsByUserType } from '@/lib/ai/entitlements';
import { postRequestBodySchema, type PostRequestBody } from './schema';
import { geolocation } from '@vercel/functions';
import {
createResumableStreamContext,
type ResumableStreamContext,
} from 'resumable-stream';
import { after } from 'next/server';
import { ChatSDKError } from '@/lib/errors';
import type { ChatMessage } from '@/lib/types';
import type { ChatModel } from '@/lib/ai/models';
import type { VisibilityType } from '@/components/visibility-selector';
import { unstable_cache as cache } from 'next/cache';
import { fetchModels } from 'tokenlens/fetch';
import { getUsage } from 'tokenlens/helpers';
import type { ModelCatalog } from 'tokenlens/core';
import type { AppUsage } from '@/lib/usage';
updateChatLastContextById,
} from "@/lib/db/queries";
import { ChatSDKError } from "@/lib/errors";
import type { ChatMessage } from "@/lib/types";
import type { AppUsage } from "@/lib/usage";
import { convertToUIMessages, generateUUID } from "@/lib/utils";
import { generateTitleFromUserMessage } from "../../actions";
import { type PostRequestBody, postRequestBodySchema } from "./schema";
export const maxDuration = 60;
@ -54,14 +54,14 @@ const getTokenlensCatalog = cache(
return await fetchModels();
} catch (err) {
console.warn(
'TokenLens: catalog fetch failed, using default catalog',
err,
"TokenLens: catalog fetch failed, using default catalog",
err
);
return undefined; // tokenlens helpers will fall back to defaultCatalog
return; // tokenlens helpers will fall back to defaultCatalog
}
},
['tokenlens-catalog'],
{ revalidate: 24 * 60 * 60 }, // 24 hours
["tokenlens-catalog"],
{ revalidate: 24 * 60 * 60 } // 24 hours
);
export function getStreamContext() {
@ -71,9 +71,9 @@ export function getStreamContext() {
waitUntil: after,
});
} catch (error: any) {
if (error.message.includes('REDIS_URL')) {
if (error.message.includes("REDIS_URL")) {
console.log(
' > Resumable streams are disabled due to missing REDIS_URL',
" > Resumable streams are disabled due to missing REDIS_URL"
);
} else {
console.error(error);
@ -91,7 +91,7 @@ export async function POST(request: Request) {
const json = await request.json();
requestBody = postRequestBodySchema.parse(json);
} catch (_) {
return new ChatSDKError('bad_request:api').toResponse();
return new ChatSDKError("bad_request:api").toResponse();
}
try {
@ -103,14 +103,14 @@ export async function POST(request: Request) {
}: {
id: string;
message: ChatMessage;
selectedChatModel: ChatModel['id'];
selectedChatModel: ChatModel["id"];
selectedVisibilityType: VisibilityType;
} = requestBody;
const session = await auth();
if (!session?.user) {
return new ChatSDKError('unauthorized:chat').toResponse();
return new ChatSDKError("unauthorized:chat").toResponse();
}
const userType: UserType = session.user.type;
@ -121,12 +121,16 @@ export async function POST(request: Request) {
});
if (messageCount > entitlementsByUserType[userType].maxMessagesPerDay) {
return new ChatSDKError('rate_limit:chat').toResponse();
return new ChatSDKError("rate_limit:chat").toResponse();
}
const chat = await getChatById({ id });
if (!chat) {
if (chat) {
if (chat.userId !== session.user.id) {
return new ChatSDKError("forbidden:chat").toResponse();
}
} else {
const title = await generateTitleFromUserMessage({
message,
});
@ -137,10 +141,6 @@ export async function POST(request: Request) {
title,
visibility: selectedVisibilityType,
});
} else {
if (chat.userId !== session.user.id) {
return new ChatSDKError('forbidden:chat').toResponse();
}
}
const messagesFromDb = await getMessagesByChatId({ id });
@ -160,7 +160,7 @@ export async function POST(request: Request) {
{
chatId: id,
id: message.id,
role: 'user',
role: "user",
parts: message.parts,
attachments: [],
createdAt: new Date(),
@ -181,15 +181,15 @@ export async function POST(request: Request) {
messages: convertToModelMessages(uiMessages),
stopWhen: stepCountIs(5),
experimental_activeTools:
selectedChatModel === 'chat-model-reasoning'
selectedChatModel === "chat-model-reasoning"
? []
: [
'getWeather',
'createDocument',
'updateDocument',
'requestSuggestions',
"getWeather",
"createDocument",
"updateDocument",
"requestSuggestions",
],
experimental_transform: smoothStream({ chunking: 'word' }),
experimental_transform: smoothStream({ chunking: "word" }),
tools: {
getWeather,
createDocument: createDocument({ session, dataStream }),
@ -201,7 +201,7 @@ export async function POST(request: Request) {
},
experimental_telemetry: {
isEnabled: isProductionEnvironment,
functionId: 'stream-text',
functionId: "stream-text",
},
onFinish: async ({ usage }) => {
try {
@ -210,23 +210,29 @@ export async function POST(request: Request) {
myProvider.languageModel(selectedChatModel).modelId;
if (!modelId) {
finalMergedUsage = usage;
dataStream.write({ type: 'data-usage', data: finalMergedUsage });
dataStream.write({
type: "data-usage",
data: finalMergedUsage,
});
return;
}
if (!providers) {
finalMergedUsage = usage;
dataStream.write({ type: 'data-usage', data: finalMergedUsage });
dataStream.write({
type: "data-usage",
data: finalMergedUsage,
});
return;
}
const summary = getUsage({ modelId, usage, providers });
finalMergedUsage = { ...usage, ...summary, modelId } as AppUsage;
dataStream.write({ type: 'data-usage', data: finalMergedUsage });
dataStream.write({ type: "data-usage", data: finalMergedUsage });
} catch (err) {
console.warn('TokenLens enrichment failed', err);
console.warn("TokenLens enrichment failed", err);
finalMergedUsage = usage;
dataStream.write({ type: 'data-usage', data: finalMergedUsage });
dataStream.write({ type: "data-usage", data: finalMergedUsage });
}
},
});
@ -236,16 +242,16 @@ export async function POST(request: Request) {
dataStream.merge(
result.toUIMessageStream({
sendReasoning: true,
}),
})
);
},
generateId: generateUUID,
onFinish: async ({ messages }) => {
await saveMessages({
messages: messages.map((message) => ({
id: message.id,
role: message.role,
parts: message.parts,
messages: messages.map((currentMessage) => ({
id: currentMessage.id,
role: currentMessage.role,
parts: currentMessage.parts,
createdAt: new Date(),
attachments: [],
chatId: id,
@ -259,12 +265,12 @@ export async function POST(request: Request) {
context: finalMergedUsage,
});
} catch (err) {
console.warn('Unable to persist last usage for chat', id, err);
console.warn("Unable to persist last usage for chat", id, err);
}
}
},
onError: () => {
return 'Oops, an error occurred!';
return "Oops, an error occurred!";
},
});
@ -273,12 +279,12 @@ export async function POST(request: Request) {
if (streamContext) {
return new Response(
await streamContext.resumableStream(streamId, () =>
stream.pipeThrough(new JsonToSseTransformStream()),
),
stream.pipeThrough(new JsonToSseTransformStream())
)
);
} else {
return new Response(stream.pipeThrough(new JsonToSseTransformStream()));
}
return new Response(stream.pipeThrough(new JsonToSseTransformStream()));
} catch (error) {
if (error instanceof ChatSDKError) {
return error.toResponse();
@ -288,35 +294,35 @@ export async function POST(request: Request) {
if (
error instanceof Error &&
error.message?.includes(
'AI Gateway requires a valid credit card on file to service requests',
"AI Gateway requires a valid credit card on file to service requests"
)
) {
return new ChatSDKError('bad_request:activate_gateway').toResponse();
return new ChatSDKError("bad_request:activate_gateway").toResponse();
}
console.error('Unhandled error in chat API:', error);
return new ChatSDKError('offline:chat').toResponse();
console.error("Unhandled error in chat API:", error);
return new ChatSDKError("offline:chat").toResponse();
}
}
export async function DELETE(request: Request) {
const { searchParams } = new URL(request.url);
const id = searchParams.get('id');
const id = searchParams.get("id");
if (!id) {
return new ChatSDKError('bad_request:api').toResponse();
return new ChatSDKError("bad_request:api").toResponse();
}
const session = await auth();
if (!session?.user) {
return new ChatSDKError('unauthorized:chat').toResponse();
return new ChatSDKError("unauthorized:chat").toResponse();
}
const chat = await getChatById({ id });
if (chat?.userId !== session.user.id) {
return new ChatSDKError('forbidden:chat').toResponse();
return new ChatSDKError("forbidden:chat").toResponse();
}
const deletedChat = await deleteChatById({ id });

View file

@ -1,13 +1,13 @@
import { z } from 'zod';
import { z } from "zod";
const textPartSchema = z.object({
type: z.enum(['text']),
type: z.enum(["text"]),
text: z.string().min(1).max(2000),
});
const filePartSchema = z.object({
type: z.enum(['file']),
mediaType: z.enum(['image/jpeg', 'image/png']),
type: z.enum(["file"]),
mediaType: z.enum(["image/jpeg", "image/png"]),
name: z.string().min(1).max(100),
url: z.string().url(),
});
@ -18,11 +18,11 @@ export const postRequestBodySchema = z.object({
id: z.string().uuid(),
message: z.object({
id: z.string().uuid(),
role: z.enum(['user']),
role: z.enum(["user"]),
parts: z.array(partSchema),
}),
selectedChatModel: z.enum(['chat-model', 'chat-model-reasoning']),
selectedVisibilityType: z.enum(['public', 'private']),
selectedChatModel: z.enum(["chat-model", "chat-model-reasoning"]),
selectedVisibilityType: z.enum(["public", "private"]),
});
export type PostRequestBody = z.infer<typeof postRequestBodySchema>;

View file

@ -1,27 +1,27 @@
import { auth } from '@/app/(auth)/auth';
import type { ArtifactKind } from '@/components/artifact';
import { auth } from "@/app/(auth)/auth";
import type { ArtifactKind } from "@/components/artifact";
import {
deleteDocumentsByIdAfterTimestamp,
getDocumentsById,
saveDocument,
} from '@/lib/db/queries';
import { ChatSDKError } from '@/lib/errors';
} from "@/lib/db/queries";
import { ChatSDKError } from "@/lib/errors";
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const id = searchParams.get('id');
const id = searchParams.get("id");
if (!id) {
return new ChatSDKError(
'bad_request:api',
'Parameter id is missing',
"bad_request:api",
"Parameter id is missing"
).toResponse();
}
const session = await auth();
if (!session?.user) {
return new ChatSDKError('unauthorized:document').toResponse();
return new ChatSDKError("unauthorized:document").toResponse();
}
const documents = await getDocumentsById({ id });
@ -29,11 +29,11 @@ export async function GET(request: Request) {
const [document] = documents;
if (!document) {
return new ChatSDKError('not_found:document').toResponse();
return new ChatSDKError("not_found:document").toResponse();
}
if (document.userId !== session.user.id) {
return new ChatSDKError('forbidden:document').toResponse();
return new ChatSDKError("forbidden:document").toResponse();
}
return Response.json(documents, { status: 200 });
@ -41,19 +41,19 @@ export async function GET(request: Request) {
export async function POST(request: Request) {
const { searchParams } = new URL(request.url);
const id = searchParams.get('id');
const id = searchParams.get("id");
if (!id) {
return new ChatSDKError(
'bad_request:api',
'Parameter id is required.',
"bad_request:api",
"Parameter id is required."
).toResponse();
}
const session = await auth();
if (!session?.user) {
return new ChatSDKError('not_found:document').toResponse();
return new ChatSDKError("not_found:document").toResponse();
}
const {
@ -66,10 +66,10 @@ export async function POST(request: Request) {
const documents = await getDocumentsById({ id });
if (documents.length > 0) {
const [document] = documents;
const [doc] = documents;
if (document.userId !== session.user.id) {
return new ChatSDKError('forbidden:document').toResponse();
if (doc.userId !== session.user.id) {
return new ChatSDKError("forbidden:document").toResponse();
}
}
@ -86,27 +86,27 @@ export async function POST(request: Request) {
export async function DELETE(request: Request) {
const { searchParams } = new URL(request.url);
const id = searchParams.get('id');
const timestamp = searchParams.get('timestamp');
const id = searchParams.get("id");
const timestamp = searchParams.get("timestamp");
if (!id) {
return new ChatSDKError(
'bad_request:api',
'Parameter id is required.',
"bad_request:api",
"Parameter id is required."
).toResponse();
}
if (!timestamp) {
return new ChatSDKError(
'bad_request:api',
'Parameter timestamp is required.',
"bad_request:api",
"Parameter timestamp is required."
).toResponse();
}
const session = await auth();
if (!session?.user) {
return new ChatSDKError('unauthorized:document').toResponse();
return new ChatSDKError("unauthorized:document").toResponse();
}
const documents = await getDocumentsById({ id });
@ -114,7 +114,7 @@ export async function DELETE(request: Request) {
const [document] = documents;
if (document.userId !== session.user.id) {
return new ChatSDKError('forbidden:document').toResponse();
return new ChatSDKError("forbidden:document").toResponse();
}
const documentsDeleted = await deleteDocumentsByIdAfterTimestamp({

View file

@ -1,19 +1,19 @@
import { put } from '@vercel/blob';
import { NextResponse } from 'next/server';
import { z } from 'zod';
import { put } from "@vercel/blob";
import { NextResponse } from "next/server";
import { z } from "zod";
import { auth } from '@/app/(auth)/auth';
import { auth } from "@/app/(auth)/auth";
// Use Blob instead of File since File is not available in Node.js environment
const FileSchema = z.object({
file: z
.instanceof(Blob)
.refine((file) => file.size <= 5 * 1024 * 1024, {
message: 'File size should be less than 5MB',
message: "File size should be less than 5MB",
})
// Update the file type based on the kind of files you want to accept
.refine((file) => ['image/jpeg', 'image/png'].includes(file.type), {
message: 'File type should be JPEG or PNG',
.refine((file) => ["image/jpeg", "image/png"].includes(file.type), {
message: "File type should be JPEG or PNG",
}),
});
@ -21,19 +21,19 @@ export async function POST(request: Request) {
const session = await auth();
if (!session) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
if (request.body === null) {
return new Response('Request body is empty', { status: 400 });
return new Response("Request body is empty", { status: 400 });
}
try {
const formData = await request.formData();
const file = formData.get('file') as Blob;
const file = formData.get("file") as Blob;
if (!file) {
return NextResponse.json({ error: 'No file uploaded' }, { status: 400 });
return NextResponse.json({ error: "No file uploaded" }, { status: 400 });
}
const validatedFile = FileSchema.safeParse({ file });
@ -41,28 +41,28 @@ export async function POST(request: Request) {
if (!validatedFile.success) {
const errorMessage = validatedFile.error.errors
.map((error) => error.message)
.join(', ');
.join(", ");
return NextResponse.json({ error: errorMessage }, { status: 400 });
}
// Get filename from formData since Blob doesn't have name property
const filename = (formData.get('file') as File).name;
const filename = (formData.get("file") as File).name;
const fileBuffer = await file.arrayBuffer();
try {
const data = await put(`${filename}`, fileBuffer, {
access: 'public',
access: "public",
});
return NextResponse.json(data);
} catch (error) {
return NextResponse.json({ error: 'Upload failed' }, { status: 500 });
} catch (_error) {
return NextResponse.json({ error: "Upload failed" }, { status: 500 });
}
} catch (error) {
} catch (_error) {
return NextResponse.json(
{ error: 'Failed to process request' },
{ status: 500 },
{ error: "Failed to process request" },
{ status: 500 }
);
}
}

View file

@ -1,26 +1,26 @@
import { auth } from '@/app/(auth)/auth';
import type { NextRequest } from 'next/server';
import { getChatsByUserId } from '@/lib/db/queries';
import { ChatSDKError } from '@/lib/errors';
import type { NextRequest } from "next/server";
import { auth } from "@/app/(auth)/auth";
import { getChatsByUserId } from "@/lib/db/queries";
import { ChatSDKError } from "@/lib/errors";
export async function GET(request: NextRequest) {
const { searchParams } = request.nextUrl;
const limit = Number.parseInt(searchParams.get('limit') || '10');
const startingAfter = searchParams.get('starting_after');
const endingBefore = searchParams.get('ending_before');
const limit = Number.parseInt(searchParams.get("limit") || "10", 10);
const startingAfter = searchParams.get("starting_after");
const endingBefore = searchParams.get("ending_before");
if (startingAfter && endingBefore) {
return new ChatSDKError(
'bad_request:api',
'Only one of starting_after or ending_before can be provided.',
"bad_request:api",
"Only one of starting_after or ending_before can be provided."
).toResponse();
}
const session = await auth();
if (!session?.user) {
return new ChatSDKError('unauthorized:chat').toResponse();
return new ChatSDKError("unauthorized:chat").toResponse();
}
const chats = await getChatsByUserId({

View file

@ -1,22 +1,22 @@
import { auth } from '@/app/(auth)/auth';
import { getSuggestionsByDocumentId } from '@/lib/db/queries';
import { ChatSDKError } from '@/lib/errors';
import { auth } from "@/app/(auth)/auth";
import { getSuggestionsByDocumentId } from "@/lib/db/queries";
import { ChatSDKError } from "@/lib/errors";
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const documentId = searchParams.get('documentId');
const documentId = searchParams.get("documentId");
if (!documentId) {
return new ChatSDKError(
'bad_request:api',
'Parameter documentId is required.',
"bad_request:api",
"Parameter documentId is required."
).toResponse();
}
const session = await auth();
if (!session?.user) {
return new ChatSDKError('unauthorized:suggestions').toResponse();
return new ChatSDKError("unauthorized:suggestions").toResponse();
}
const suggestions = await getSuggestionsByDocumentId({
@ -30,7 +30,7 @@ export async function GET(request: Request) {
}
if (suggestion.userId !== session.user.id) {
return new ChatSDKError('forbidden:api').toResponse();
return new ChatSDKError("forbidden:api").toResponse();
}
return Response.json(suggestions, { status: 200 });

View file

@ -1,32 +1,32 @@
import { auth } from '@/app/(auth)/auth';
import { getChatById, getVotesByChatId, voteMessage } from '@/lib/db/queries';
import { ChatSDKError } from '@/lib/errors';
import { auth } from "@/app/(auth)/auth";
import { getChatById, getVotesByChatId, voteMessage } from "@/lib/db/queries";
import { ChatSDKError } from "@/lib/errors";
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const chatId = searchParams.get('chatId');
const chatId = searchParams.get("chatId");
if (!chatId) {
return new ChatSDKError(
'bad_request:api',
'Parameter chatId is required.',
"bad_request:api",
"Parameter chatId is required."
).toResponse();
}
const session = await auth();
if (!session?.user) {
return new ChatSDKError('unauthorized:vote').toResponse();
return new ChatSDKError("unauthorized:vote").toResponse();
}
const chat = await getChatById({ id: chatId });
if (!chat) {
return new ChatSDKError('not_found:chat').toResponse();
return new ChatSDKError("not_found:chat").toResponse();
}
if (chat.userId !== session.user.id) {
return new ChatSDKError('forbidden:vote').toResponse();
return new ChatSDKError("forbidden:vote").toResponse();
}
const votes = await getVotesByChatId({ id: chatId });
@ -39,37 +39,37 @@ export async function PATCH(request: Request) {
chatId,
messageId,
type,
}: { chatId: string; messageId: string; type: 'up' | 'down' } =
}: { chatId: string; messageId: string; type: "up" | "down" } =
await request.json();
if (!chatId || !messageId || !type) {
return new ChatSDKError(
'bad_request:api',
'Parameters chatId, messageId, and type are required.',
"bad_request:api",
"Parameters chatId, messageId, and type are required."
).toResponse();
}
const session = await auth();
if (!session?.user) {
return new ChatSDKError('unauthorized:vote').toResponse();
return new ChatSDKError("unauthorized:vote").toResponse();
}
const chat = await getChatById({ id: chatId });
if (!chat) {
return new ChatSDKError('not_found:vote').toResponse();
return new ChatSDKError("not_found:vote").toResponse();
}
if (chat.userId !== session.user.id) {
return new ChatSDKError('forbidden:vote').toResponse();
return new ChatSDKError("forbidden:vote").toResponse();
}
await voteMessage({
chatId,
messageId,
type: type,
type,
});
return new Response('Message voted', { status: 200 });
return new Response("Message voted", { status: 200 });
}

View file

@ -1,12 +1,12 @@
import { cookies } from 'next/headers';
import { notFound, redirect } from 'next/navigation';
import { cookies } from "next/headers";
import { notFound, redirect } from "next/navigation";
import { auth } from '@/app/(auth)/auth';
import { Chat } from '@/components/chat';
import { getChatById, getMessagesByChatId } from '@/lib/db/queries';
import { DataStreamHandler } from '@/components/data-stream-handler';
import { DEFAULT_CHAT_MODEL } from '@/lib/ai/models';
import { convertToUIMessages } from '@/lib/utils';
import { auth } from "@/app/(auth)/auth";
import { Chat } from "@/components/chat";
import { DataStreamHandler } from "@/components/data-stream-handler";
import { DEFAULT_CHAT_MODEL } from "@/lib/ai/models";
import { getChatById, getMessagesByChatId } from "@/lib/db/queries";
import { convertToUIMessages } from "@/lib/utils";
export default async function Page(props: { params: Promise<{ id: string }> }) {
const params = await props.params;
@ -20,10 +20,10 @@ export default async function Page(props: { params: Promise<{ id: string }> }) {
const session = await auth();
if (!session) {
redirect('/api/auth/guest');
redirect("/api/auth/guest");
}
if (chat.visibility === 'private') {
if (chat.visibility === "private") {
if (!session.user) {
return notFound();
}
@ -40,20 +40,19 @@ export default async function Page(props: { params: Promise<{ id: string }> }) {
const uiMessages = convertToUIMessages(messagesFromDb);
const cookieStore = await cookies();
const chatModelFromCookie = cookieStore.get('chat-model');
const chatModelFromCookie = cookieStore.get("chat-model");
if (!chatModelFromCookie) {
return (
<>
<Chat
autoResume={true}
id={chat.id}
initialMessages={uiMessages}
initialChatModel={DEFAULT_CHAT_MODEL}
initialLastContext={chat.lastContext ?? undefined}
initialMessages={uiMessages}
initialVisibilityType={chat.visibility}
isReadonly={session?.user?.id !== chat.userId}
session={session}
autoResume={true}
initialLastContext={chat.lastContext ?? undefined}
/>
<DataStreamHandler />
</>
@ -63,14 +62,13 @@ export default async function Page(props: { params: Promise<{ id: string }> }) {
return (
<>
<Chat
autoResume={true}
id={chat.id}
initialMessages={uiMessages}
initialChatModel={chatModelFromCookie.value}
initialLastContext={chat.lastContext ?? undefined}
initialMessages={uiMessages}
initialVisibilityType={chat.visibility}
isReadonly={session?.user?.id !== chat.userId}
session={session}
autoResume={true}
initialLastContext={chat.lastContext ?? undefined}
/>
<DataStreamHandler />
</>

View file

@ -1,10 +1,9 @@
import { cookies } from 'next/headers';
import { AppSidebar } from '@/components/app-sidebar';
import { SidebarInset, SidebarProvider } from '@/components/ui/sidebar';
import { auth } from '../(auth)/auth';
import Script from 'next/script';
import { DataStreamProvider } from '@/components/data-stream-provider';
import { cookies } from "next/headers";
import Script from "next/script";
import { AppSidebar } from "@/components/app-sidebar";
import { DataStreamProvider } from "@/components/data-stream-provider";
import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar";
import { auth } from "../(auth)/auth";
export const experimental_ppr = true;
@ -14,7 +13,7 @@ export default async function Layout({
children: React.ReactNode;
}) {
const [session, cookieStore] = await Promise.all([auth(), cookies()]);
const isCollapsed = cookieStore.get('sidebar:state')?.value !== 'true';
const isCollapsed = cookieStore.get("sidebar_state")?.value !== "true";
return (
<>

View file

@ -1,36 +1,34 @@
import { cookies } from 'next/headers';
import { Chat } from '@/components/chat';
import { DEFAULT_CHAT_MODEL } from '@/lib/ai/models';
import { generateUUID } from '@/lib/utils';
import { DataStreamHandler } from '@/components/data-stream-handler';
import { auth } from '../(auth)/auth';
import { redirect } from 'next/navigation';
import { cookies } from "next/headers";
import { redirect } from "next/navigation";
import { Chat } from "@/components/chat";
import { DataStreamHandler } from "@/components/data-stream-handler";
import { DEFAULT_CHAT_MODEL } from "@/lib/ai/models";
import { generateUUID } from "@/lib/utils";
import { auth } from "../(auth)/auth";
export default async function Page() {
const session = await auth();
if (!session) {
redirect('/api/auth/guest');
redirect("/api/auth/guest");
}
const id = generateUUID();
const cookieStore = await cookies();
const modelIdFromCookie = cookieStore.get('chat-model');
const modelIdFromCookie = cookieStore.get("chat-model");
if (!modelIdFromCookie) {
return (
<>
<Chat
key={id}
autoResume={false}
id={id}
initialMessages={[]}
initialChatModel={DEFAULT_CHAT_MODEL}
initialMessages={[]}
initialVisibilityType="private"
isReadonly={false}
session={session}
autoResume={false}
key={id}
/>
<DataStreamHandler />
</>
@ -40,14 +38,13 @@ export default async function Page() {
return (
<>
<Chat
key={id}
autoResume={false}
id={id}
initialMessages={[]}
initialChatModel={modelIdFromCookie.value}
initialMessages={[]}
initialVisibilityType="private"
isReadonly={false}
session={session}
autoResume={false}
key={id}
/>
<DataStreamHandler />
</>