Skip to content
Open
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
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,10 @@ yarn-error.log*
/playwright-report/
/blob-report/
/playwright/*


package-lock.json

**/*backup*
**/*ignore*
!.gitignore
6 changes: 3 additions & 3 deletions app/(auth)/api/auth/guest/route.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { NextResponse } from "next/server";
import { getToken } from "next-auth/jwt";
import { signIn } from "@/app/(auth)/auth";
import { isDevelopmentEnvironment } from "@/lib/constants";
import { getToken } from "next-auth/jwt";
import { NextRequest, NextResponse } from "next/server";

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

Expand Down
41 changes: 38 additions & 3 deletions app/(auth)/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,27 @@ declare module "next-auth" {
user: {
id: string;
type: UserType;
isLoggedIn: boolean;
username?: string|null;
} & DefaultSession["user"];
}

interface User {
id?: string;
email?: string | null;
type: UserType;
username?: string|null;
isLoggedIn: boolean;
}
}

declare module "next-auth/jwt" {
interface JWT extends DefaultJWT {
id: string;
type: UserType;
email?: string;
username?: string;
isLoggedIn: boolean;
}
}

Expand Down Expand Up @@ -61,15 +68,29 @@ export const {
return null;
}

return { ...user, type: "regular" };
return { ...user, type: "regular", isLoggedIn: true };
},
}),
Credentials({
id: "guest",
credentials: {},
async authorize() {
const [guestUser] = await createGuestUser();
return { ...guestUser, type: "guest" };
try {
// Create guest user
const [guestUser] = await createGuestUser();

return {
...guestUser,
type: "guest",
isLoggedIn: false
};
} catch (error) {
console.error("Guest auth error: Unexpected error during guest authentication:", {
error: error instanceof Error ? error.message : 'Unknown error',
stack: error instanceof Error ? error.stack : undefined
});
return null;
}
},
}),
],
Expand All @@ -78,6 +99,13 @@ export const {
if (user) {
token.id = user.id as string;
token.type = user.type;
token.isLoggedIn = user.isLoggedIn;
if (user.email) {
token.email = user.email;
}
if (user.username) {
token.username = user.username;
}
}

return token;
Expand All @@ -86,6 +114,13 @@ export const {
if (session.user) {
session.user.id = token.id;
session.user.type = token.type;
session.user.isLoggedIn = token.isLoggedIn;
if (token.email) {
session.user.email = token.email;
}
if (token.username) {
session.user.username = token.username;
}
}

return session;
Expand Down
16 changes: 15 additions & 1 deletion app/(chat)/api/chat/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,21 @@ export async function POST(request: Request) {
title: "New chat",
visibility: selectedVisibilityType,
});
titlePromise = generateTitleFromUserMessage({ message });
// titlePromise = generateTitleFromUserMessage({ message });
// Convert message to proper ChatMessage format by filtering out undefined text parts
const cleanedMessage: ChatMessage = {
...message,
parts: message.parts
.filter(part => part.type === 'file' || (part.type === 'text' && part.text !== undefined))
.map(part => {
if (part.type === 'text') {
return { type: 'text', text: part.text! };
}
return part;
})
};
titlePromise = generateTitleFromUserMessage({ message: cleanedMessage });

}

const uiMessages = isToolApprovalFlow
Expand Down
24 changes: 23 additions & 1 deletion app/(chat)/api/files/upload/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ import { NextResponse } from "next/server";
import { z } from "zod";

import { auth } from "@/app/(auth)/auth";
import { ChatbotError } from "@/lib/errors";
import { filterImage } from "@/lib/image-filter";

// Storage on Vercel Blob
// Use Blob instead of File since File is not available in Node.js environment
const FileSchema = z.object({
file: z
Expand Down Expand Up @@ -49,9 +52,24 @@ export async function POST(request: Request) {
// Get filename from formData since Blob doesn't have name property
const filename = (formData.get("file") as File).name;
const fileBuffer = await file.arrayBuffer();
const buffer = Buffer.from(fileBuffer);

// Check if user is guest
const isGuest = !!session.user.email?.startsWith('guest-') && session.user.type !== "regular";

if (process.env.OPENAI_API_KEY) {
// Filter image for inappropriate content
const filterResult = await filterImage(buffer, isGuest);

if (!filterResult.isAllowed) {
return NextResponse.json({
error: filterResult.reason || "Image validation failed"
}, { status: 400 });
}
}

try {
const data = await put(`${filename}`, fileBuffer, {
const data = await put(`${filename}`, buffer, {
access: "public",
});

Expand All @@ -60,6 +78,10 @@ export async function POST(request: Request) {
return NextResponse.json({ error: "Upload failed" }, { status: 500 });
}
} catch (_error) {
if (_error instanceof ChatbotError) {
return _error.toResponse();
}

return NextResponse.json(
{ error: "Failed to process request" },
{ status: 500 }
Expand Down
26 changes: 26 additions & 0 deletions app/(chat)/api/rename-chat/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { auth } from "@/app/(auth)/auth";
import { updateChatTitleById } from "@/lib/db/queries";
import { NextRequest, NextResponse } from "next/server";

export async function PATCH(request: NextRequest) {
try {
const session = await auth();

if (!session?.user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}

const { chatId, title } = await request.json();

if (!chatId || !title) {
return NextResponse.json({ error: 'Missing chatId or title' }, { status: 400 });
}

await updateChatTitleById({ chatId, title });

return NextResponse.json({ success: true, title });
} catch (error) {
console.error('Error renaming chat:', error);
return NextResponse.json({ error: 'Failed to rename chat' }, { status: 500 });
}
}
11 changes: 8 additions & 3 deletions app/(chat)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ 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";
import { AuthVerificationWrapper } from "@/components/chat-resizable-layout";

export default function Layout({ children }: { children: React.ReactNode }) {
return (
Expand All @@ -23,13 +24,17 @@ export default function Layout({ children }: { children: React.ReactNode }) {
}

async function SidebarWrapper({ children }: { children: React.ReactNode }) {
const [session, cookieStore] = await Promise.all([auth(), cookies()]);
// const [session, cookieStore] = await Promise.all([auth(), cookies()]);
const cookieStore = await cookies();
const isCollapsed = cookieStore.get("sidebar_state")?.value !== "true";

return (
<SidebarProvider defaultOpen={!isCollapsed}>
<AppSidebar user={session?.user} />
<SidebarInset>{children}</SidebarInset>
{/* <AppSidebar user={session?.user} />
<SidebarInset>{children}</SidebarInset> */}
<AuthVerificationWrapper isCollapsed={isCollapsed}>
{children}
</AuthVerificationWrapper>
</SidebarProvider>
);
}
4 changes: 4 additions & 0 deletions components/chat-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { Button } from "@/components/ui/button";
import { PlusIcon, VercelIcon } from "./icons";
import { useSidebar } from "./ui/sidebar";
import { VisibilitySelector, type VisibilityType } from "./visibility-selector";
import { ThemeToggle } from "./theme-toggle";

function PureChatHeader({
chatId,
Expand Down Expand Up @@ -50,6 +51,9 @@ function PureChatHeader({
/>
)}

<ThemeToggle />


<Button
asChild
className="order-3 hidden bg-zinc-900 px-2 text-zinc-50 hover:bg-zinc-800 md:ml-auto md:flex md:h-fit dark:bg-zinc-100 dark:text-zinc-900 dark:hover:bg-zinc-200"
Expand Down
Loading