diff --git a/app/actions.ts b/app/actions.ts index 3d40e88..075918f 100644 --- a/app/actions.ts +++ b/app/actions.ts @@ -4,6 +4,7 @@ import { revalidatePath } from 'next/cache' import { kv } from '@vercel/kv' import { type Chat } from '@/lib/types' +import { currentUser } from '@clerk/nextjs' export async function getChats(userId?: string | null) { if (!userId) { @@ -40,24 +41,19 @@ export async function getChat(id: string, userId: string) { return chat } -export async function removeChat({ - id, - path, - userId -}: { - id: string - userId: string - path: string -}) { - // @todo next-auth@v5 doesn't work in server actions yet - // const session = await auth(); +export async function removeChat({ id, path }: { id: string; path: string }) { + const user = await currentUser() + + if (!user) { + throw new Error('Unauthorized') + } const uid = await kv.hget(`chat:${id}`, 'userId') - if (uid !== userId) { + if (uid !== user.id) { throw new Error('Unauthorized') } await kv.del(`chat:${id}`) - await kv.zrem(`user:chat:${userId}`, `chat:${id}`) + await kv.zrem(`user:chat:${user.id}`, `chat:${id}`) revalidatePath('/') revalidatePath('/chat/[id]') diff --git a/app/api/auth/[...nextauth]/route.ts b/app/api/auth/[...nextauth]/route.ts deleted file mode 100644 index 4463a8e..0000000 --- a/app/api/auth/[...nextauth]/route.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { GET, POST } from '@/auth' -// export const runtime = 'edge' diff --git a/app/api/chat/route.ts b/app/api/chat/route.ts index 3a91d6e..6feb8e5 100644 --- a/app/api/chat/route.ts +++ b/app/api/chat/route.ts @@ -1,11 +1,11 @@ -import { auth } from '@/auth' import { kv } from '@vercel/kv' import { OpenAIStream, StreamingTextResponse } from 'ai-connector' import { Configuration, OpenAIApi } from 'openai-edge' import { nanoid } from '@/lib/utils' +import { currentUser } from '@clerk/nextjs' -// export const runtime = 'edge' +export const runtime = 'edge' const configuration = new Configuration({ apiKey: process.env.OPENAI_API_KEY @@ -17,13 +17,15 @@ if (!process.env.OPENAI_API_KEY) { throw new Error('Missing env var from OpenAI') } -export const POST = auth(async function POST(req: Request) { +export async function POST(req: Request) { + const user = await currentUser() + if (user == null) { + return new Response('Unauthorized', { status: 401 }) + } + const json = await req.json() - // @ts-ignore - const messages = json.messages.map((m: any) => ({ - content: m.content, - role: m.role - })) + const { messages } = json + const res = await openai.createChatCompletion({ model: 'gpt-3.5-turbo', messages, @@ -35,14 +37,10 @@ export const POST = auth(async function POST(req: Request) { stream: true }) - const stream = await OpenAIStream(res, { + const stream = OpenAIStream(res, { async onCompletion(completion) { - // @ts-ignore - if (req.auth?.user?.email == null) { - return - } const title = json.messages[0].content.substring(0, 100) - const userId = (req as any).auth?.user?.email + const userId = user.id const id = json.id ?? nanoid() const createdAt = Date.now() const payload = { @@ -67,4 +65,4 @@ export const POST = auth(async function POST(req: Request) { }) return new StreamingTextResponse(stream) -}) +} diff --git a/app/chat/[id]/page.tsx b/app/chat/[id]/page.tsx index 728ef16..a125141 100644 --- a/app/chat/[id]/page.tsx +++ b/app/chat/[id]/page.tsx @@ -1,8 +1,9 @@ import { type Metadata } from 'next' -import { auth } from '@/auth' import { Chat } from '@/components/chat' import { getChat } from '@/app/actions' +import { Header } from '@/components/header' +import { auth } from '@clerk/nextjs' // export const runtime = 'edge' export const preferredRegion = 'home' @@ -16,16 +17,24 @@ export interface ChatPageProps { export async function generateMetadata({ params }: ChatPageProps): Promise { - const session = await auth() - const chat = await getChat(params.id, session?.user?.email ?? '') + const { user } = await auth() + const chat = await getChat(params.id, user?.id ?? '') return { title: chat?.title.slice(0, 50) ?? 'Chat' } } export default async function ChatPage({ params }: ChatPageProps) { - const session = await auth() - const chat = await getChat(params.id, session?.user?.email ?? '') + const { user } = await auth() + const chat = await getChat(params.id, user?.id ?? '') - return + return ( +
+ {/* @ts-ignore */} +
+
+ +
+
+ ) } diff --git a/app/layout.tsx b/app/layout.tsx index 4d366d0..d92c416 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -6,6 +6,7 @@ import { cn } from '@/lib/utils' import { Header } from '@/components/header' import { TailwindIndicator } from '@/components/tailwind-indicator' import { ThemeProvider } from '@/components/theme-provider' +import { ClerkProvider } from '@clerk/nextjs' export const metadata: Metadata = { title: { @@ -30,24 +31,22 @@ interface RootLayoutProps { export default function RootLayout({ children }: RootLayoutProps) { return ( - - - - -
- {/* @ts-ignore */} -
-
{children}
-
- -
- - + + + + + + {children} + + + + + ) } diff --git a/app/page.tsx b/app/page.tsx index ee1ff4c..667918a 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,8 +1,17 @@ import { Chat } from '@/components/chat' +import { Header } from '@/components/header' // export const runtime = 'edge' export const preferredRegion = 'home' export default async function IndexPage() { - return + return ( +
+ {/* @ts-ignore */} +
+
+ +
+
+ ) } diff --git a/app/sign-in/[[...sign-in]]/page.tsx b/app/sign-in/[[...sign-in]]/page.tsx new file mode 100644 index 0000000..3b64227 --- /dev/null +++ b/app/sign-in/[[...sign-in]]/page.tsx @@ -0,0 +1,11 @@ +import { SignIn } from '@clerk/nextjs' + +export default function Page() { + return ( +
+
+ +
+
+ ) +} diff --git a/app/sign-up/[[...sign-up]]/page.tsx b/app/sign-up/[[...sign-up]]/page.tsx new file mode 100644 index 0000000..38e2669 --- /dev/null +++ b/app/sign-up/[[...sign-up]]/page.tsx @@ -0,0 +1,11 @@ +import { SignUp } from '@clerk/nextjs' + +export default function Page() { + return ( +
+
+ +
+
+ ) +} diff --git a/auth.ts b/auth.ts deleted file mode 100644 index aee24e3..0000000 --- a/auth.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { NextResponse } from 'next/server' -import NextAuth from '@auth/nextjs' -import GitHub from '@auth/nextjs/providers/github' - -export const { - handlers: { GET, POST }, - auth, - CSRF_experimental -} = NextAuth({ - // @ts-ignore - providers: [GitHub], - session: { strategy: 'jwt' }, - async authorized({ request, auth }: any) { - const url = request.nextUrl - - if (request.method === 'POST') { - const { authToken } = (await request.json()) ?? {} - // If the request has a valid auth token, it is authorized - const valid = true - if (valid) return true - return NextResponse.json('Invalid auth token', { status: 401 }) - } - - // Logged in users are authenticated, otherwise redirect to login page - return !!auth - } -}) diff --git a/components/chat.tsx b/components/chat.tsx index 4352cc6..0949dff 100644 --- a/components/chat.tsx +++ b/components/chat.tsx @@ -1,6 +1,6 @@ 'use client' -import { useChat, type Message } from 'ai-connector' +import { useChat, type Message } from 'ai-connector/react' import { cn } from '@/lib/utils' import { ChatList } from '@/components/chat-list' diff --git a/components/header.tsx b/components/header.tsx index d59f67a..56d03f8 100644 --- a/components/header.tsx +++ b/components/header.tsx @@ -1,29 +1,28 @@ import { Suspense } from 'react' -import { auth } from '@/auth' import { cn } from '@/lib/utils' import { buttonVariants } from '@/components/ui/button' import { Sidebar } from '@/components/sidebar' -import { UserMenu } from '@/components/user-menu' import { SidebarList } from '@/components/sidebar-list' import { IconGitHub, IconSeparator, IconVercel } from '@/components/ui/icons' +import { UserButton, currentUser } from '@clerk/nextjs' export async function Header() { - const session = await auth() + const user = await currentUser() return ( -
+
{/* @ts-ignore */} - + }> {/* @ts-ignore */} - +
- - + +
@@ -34,7 +33,7 @@ export async function Header() { className={cn(buttonVariants({ variant: 'outline' }))} > - GitHub + GitHub { setIsLoading(true) - signIn('github') + clerk.openSignIn({}) }} disabled={isLoading} className={cn(className)} diff --git a/components/sidebar-list.tsx b/components/sidebar-list.tsx index 1e2278b..ce603ca 100644 --- a/components/sidebar-list.tsx +++ b/components/sidebar-list.tsx @@ -1,17 +1,16 @@ import { getChats } from '@/app/actions' -import { Session } from '@auth/core/types' import { SidebarItem } from '@/components/sidebar-item' import { LoginButton } from '@/components/login-button' export interface SidebarListProps { - session?: Session + userId?: string } -export async function SidebarList({ session }: SidebarListProps) { - if (!session?.user?.email) { +export async function SidebarList({ userId }: SidebarListProps) { + if (!userId) { return ( -
+

You are not logged in!

@@ -23,7 +22,7 @@ export async function SidebarList({ session }: SidebarListProps) { ) } - const chats = await getChats(session.user.email) + const chats = await getChats(userId) return (

@@ -33,7 +32,7 @@ export async function SidebarList({ session }: SidebarListProps) { diff --git a/components/sidebar.tsx b/components/sidebar.tsx index a4cbbc5..9724235 100644 --- a/components/sidebar.tsx +++ b/components/sidebar.tsx @@ -1,8 +1,6 @@ 'use client' import * as React from 'react' -import { signOut } from '@auth/nextjs/client' -import { type Session } from '@auth/nextjs/types' import { Button } from '@/components/ui/button' import { @@ -14,18 +12,20 @@ import { } from '@/components/ui/sheet' import { ThemeToggle } from '@/components/theme-toggle' import { IconSidebar } from '@/components/ui/icons' +import { useClerk } from '@clerk/nextjs' export interface SidebarProps { - session?: Session + userId?: string children?: React.ReactNode } -export function Sidebar({ session, children }: SidebarProps) { +export function Sidebar({ userId, children }: SidebarProps) { + const { signOut } = useClerk() return ( - @@ -36,7 +36,7 @@ export function Sidebar({ session, children }: SidebarProps) { {children}
- {session?.user && ( + {userId && (