Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions app/(auth)/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

import { z } from "zod";

import { createUser, getUser } from "@/lib/db/queries";
import {
createPersonalizationByUserId,
createUser,
getUser,
} from "@/lib/db/queries";

import { signIn } from "./auth";

Expand Down Expand Up @@ -79,14 +83,20 @@ export const register = async (
}

// Whenever we create a new user in dev, we set the user.type to dev
await createUser({
const newUser = await createUser({
email: validatedData.email,
password: validatedData.password,
name: validatedData.name,
profileURL: null,
type: "dev",
});

await createPersonalizationByUserId({
userId: newUser.id,
name: newUser.name,
email: newUser.email,
});

await signIn("credentials", {
email: validatedData.email,
password: validatedData.password,
Expand Down
14 changes: 12 additions & 2 deletions app/(auth)/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ import NextAuth, { type DefaultSession } from "next-auth";
import type { DefaultJWT } from "next-auth/jwt";
import CredentialsProvider from "next-auth/providers/credentials";
import GoogleProvider from "next-auth/providers/google";
import { createUser, getUser } from "@/lib/db/queries";
import {
createPersonalizationByUserId,
createUser,
getUser,
} from "@/lib/db/queries";
import { authConfig } from "./auth.config";

export type UserType = "plus" | "pro" | "ultra" | "dev" | "free";
Expand Down Expand Up @@ -91,7 +95,7 @@ export const {
}

const [existingUser] = await getUser(user.email);

// for Oauth providers
if (account?.provider === "google") {
if (existingUser) {
Expand All @@ -107,6 +111,12 @@ export const {
// Set the ID and type for the newly created user
user.id = newUser.id;
user.type = newUser.type;

await createPersonalizationByUserId({
userId: newUser.id,
name: newUser.name,
email: newUser.email,
});
}
}

Expand Down
16 changes: 8 additions & 8 deletions app/(chat)/api/chat/[id]/stream/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
getStreamIdsByChatId,
} from "@/lib/db/queries";
import type { Chat } from "@/lib/db/schema";
import { ChatSDKError } from "@/lib/errors";
import { WitelyError } from "@/lib/errors";
import type { ChatMessage } from "@/lib/types";
import { getStreamContext } from "../../route";

Expand All @@ -25,41 +25,41 @@ export async function GET(
}

if (!chatId) {
return new ChatSDKError("bad_request:api").toResponse();
return new WitelyError("bad_request:api").toResponse();
}

const session = await auth();

if (!session?.user) {
return new ChatSDKError("unauthorized:chat").toResponse();
return new WitelyError("unauthorized:chat").toResponse();
}

let chat: Chat | null;

try {
chat = await getChatById({ id: chatId });
} catch {
return new ChatSDKError("not_found:chat").toResponse();
return new WitelyError("not_found:chat").toResponse();
}

if (!chat) {
return new ChatSDKError("not_found:chat").toResponse();
return new WitelyError("not_found:chat").toResponse();
}

if (chat.visibility === "private" && chat.userId !== session.user.id) {
return new ChatSDKError("forbidden:chat").toResponse();
return new WitelyError("forbidden:chat").toResponse();
}

const streamIds = await getStreamIdsByChatId({ chatId });

if (!streamIds.length) {
return new ChatSDKError("not_found:stream").toResponse();
return new WitelyError("not_found:stream").toResponse();
}

const recentStreamId = streamIds.at(-1);

if (!recentStreamId) {
return new ChatSDKError("not_found:stream").toResponse();
return new WitelyError("not_found:stream").toResponse();
}

const emptyDataStream = createUIMessageStream<ChatMessage>({
Expand Down
22 changes: 11 additions & 11 deletions app/(chat)/api/chat/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ import {
saveMessages,
updateChatLastContextById,
} from "@/lib/db/queries";
import { ChatSDKError } from "@/lib/errors";
import { WitelyError } from "@/lib/errors";
import type { ChatMessage } from "@/lib/types";
import type { AppUsage } from "@/lib/usage";
import { convertToUIMessages, generateUUID } from "@/lib/utils";
Expand Down Expand Up @@ -274,7 +274,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 WitelyError("bad_request:api").toResponse();
}

try {
Expand All @@ -293,7 +293,7 @@ export async function POST(request: Request) {
const session = await auth();

if (!session?.user) {
return new ChatSDKError("unauthorized:chat").toResponse();
return new WitelyError("unauthorized:chat").toResponse();
}

const userType: UserType = session.user.type;
Expand Down Expand Up @@ -338,14 +338,14 @@ export async function POST(request: Request) {
});

if (messageCount > entitlementsByUserType[userType].maxMessagesPerDay) {
return new ChatSDKError("rate_limit:chat").toResponse();
return new WitelyError("rate_limit:chat").toResponse();
}

const chat = await getChatById({ id });

if (chat) {
if (chat.userId !== session.user.id) {
return new ChatSDKError("forbidden:chat").toResponse();
return new WitelyError("forbidden:chat").toResponse();
}
} else {
const title = await generateTitleFromUserMessage({
Expand Down Expand Up @@ -545,7 +545,7 @@ export async function POST(request: Request) {
} catch (error) {
const vercelId = request.headers.get("x-vercel-id");

if (error instanceof ChatSDKError) {
if (error instanceof WitelyError) {
return error.toResponse();
}

Expand All @@ -556,11 +556,11 @@ export async function POST(request: Request) {
"AI Gateway requires a valid credit card on file to service requests"
)
) {
return new ChatSDKError("bad_request:activate_gateway").toResponse();
return new WitelyError("bad_request:activate_gateway").toResponse();
}

console.error("Unhandled error in chat API:", error, { vercelId });
return new ChatSDKError("offline:chat").toResponse();
return new WitelyError("offline:chat").toResponse();
}
}

Expand All @@ -569,19 +569,19 @@ export async function DELETE(request: Request) {
const id = searchParams.get("id");

if (!id) {
return new ChatSDKError("bad_request:api").toResponse();
return new WitelyError("bad_request:api").toResponse();
}

const session = await auth();

if (!session?.user) {
return new ChatSDKError("unauthorized:chat").toResponse();
return new WitelyError("unauthorized:chat").toResponse();
}

const chat = await getChatById({ id });

if (chat?.userId !== session.user.id) {
return new ChatSDKError("forbidden:chat").toResponse();
return new WitelyError("forbidden:chat").toResponse();
}

const deletedChat = await deleteChatById({ id });
Expand Down
24 changes: 12 additions & 12 deletions app/(chat)/api/document/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import {
getDocumentsById,
saveDocument,
} from "@/lib/db/queries";
import { ChatSDKError } from "@/lib/errors";
import { WitelyError } from "@/lib/errors";

export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const id = searchParams.get("id");

if (!id) {
return new ChatSDKError(
return new WitelyError(
"bad_request:api",
"Parameter id is missing"
).toResponse();
Expand All @@ -21,19 +21,19 @@ export async function GET(request: Request) {
const session = await auth();

if (!session?.user) {
return new ChatSDKError("unauthorized:document").toResponse();
return new WitelyError("unauthorized:document").toResponse();
}

const documents = await getDocumentsById({ id });

const [document] = documents;

if (!document) {
return new ChatSDKError("not_found:document").toResponse();
return new WitelyError("not_found:document").toResponse();
}

if (document.userId !== session.user.id) {
return new ChatSDKError("forbidden:document").toResponse();
return new WitelyError("forbidden:document").toResponse();
}

return Response.json(documents, { status: 200 });
Expand All @@ -44,7 +44,7 @@ export async function POST(request: Request) {
const id = searchParams.get("id");

if (!id) {
return new ChatSDKError(
return new WitelyError(
"bad_request:api",
"Parameter id is required."
).toResponse();
Expand All @@ -53,7 +53,7 @@ export async function POST(request: Request) {
const session = await auth();

if (!session?.user) {
return new ChatSDKError("not_found:document").toResponse();
return new WitelyError("not_found:document").toResponse();
}

const {
Expand All @@ -69,7 +69,7 @@ export async function POST(request: Request) {
const [doc] = documents;

if (doc.userId !== session.user.id) {
return new ChatSDKError("forbidden:document").toResponse();
return new WitelyError("forbidden:document").toResponse();
}
}

Expand All @@ -90,14 +90,14 @@ export async function DELETE(request: Request) {
const timestamp = searchParams.get("timestamp");

if (!id) {
return new ChatSDKError(
return new WitelyError(
"bad_request:api",
"Parameter id is required."
).toResponse();
}

if (!timestamp) {
return new ChatSDKError(
return new WitelyError(
"bad_request:api",
"Parameter timestamp is required."
).toResponse();
Expand All @@ -106,15 +106,15 @@ export async function DELETE(request: Request) {
const session = await auth();

if (!session?.user) {
return new ChatSDKError("unauthorized:document").toResponse();
return new WitelyError("unauthorized:document").toResponse();
}

const documents = await getDocumentsById({ id });

const [document] = documents;

if (document.userId !== session.user.id) {
return new ChatSDKError("forbidden:document").toResponse();
return new WitelyError("forbidden:document").toResponse();
}

const documentsDeleted = await deleteDocumentsByIdAfterTimestamp({
Expand Down
6 changes: 3 additions & 3 deletions app/(chat)/api/history/route.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { NextRequest } from "next/server";
import { auth } from "@/app/(auth)/auth";
import { getChatsByUserId } from "@/lib/db/queries";
import { ChatSDKError } from "@/lib/errors";
import { WitelyError } from "@/lib/errors";

export async function GET(request: NextRequest) {
const { searchParams } = request.nextUrl;
Expand All @@ -11,7 +11,7 @@ export async function GET(request: NextRequest) {
const endingBefore = searchParams.get("ending_before");

if (startingAfter && endingBefore) {
return new ChatSDKError(
return new WitelyError(
"bad_request:api",
"Only one of starting_after or ending_before can be provided."
).toResponse();
Expand All @@ -20,7 +20,7 @@ export async function GET(request: NextRequest) {
const session = await auth();

if (!session?.user) {
return new ChatSDKError("unauthorized:chat").toResponse();
return new WitelyError("unauthorized:chat").toResponse();
}

const chats = await getChatsByUserId({
Expand Down
42 changes: 42 additions & 0 deletions app/(chat)/api/personalization/bio/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { NextResponse } from "next/server";
import z from "zod";
import { auth } from "@/app/(auth)/auth";
import { getUser, updateBioByUserId } from "@/lib/db/queries";

const bioSchema = z.object({
bio: z.string().max(500),
});

export async function POST(req: Request) {
try {
const session = await auth();

if (!session?.user?.email) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
Comment on lines +14 to +16

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For consistency with other API routes in the application, it's better to use the custom WitelyError class for handling errors. This centralizes error responses and ensures a uniform API error structure. This suggestion also applies to other new API routes in this PR.

      return new WitelyError("unauthorized:auth").toResponse();


const [user] = await getUser(session.user.email);

if (!user) {
return NextResponse.json({ error: "User not found" }, { status: 404 });
}
Comment on lines +20 to +22

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To maintain consistent error handling across the API, this error should be thrown using the custom WitelyError class. This approach is used in other refactored parts of the application.

      return new WitelyError("not_found:database", "User not found").toResponse();


const { bio } = bioSchema.parse(await req.json());

await updateBioByUserId({ userId: user.id, bio });

return NextResponse.json({ success: true });
} catch (error) {
if (error instanceof z.ZodError) {
return NextResponse.json(
{ error: "Invalid input", issues: error.errors },
{ status: 400 }
);
}
console.error("Error posting user bio", error);
return NextResponse.json(
{ error: "Failed to post user bio" },
{ status: 500 }
);
}
}
Loading
Loading