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
10 changes: 1 addition & 9 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,9 @@ OPENAI_API_KEY=XXXXXXXX

# Generate a random secret: https://generate-secret.vercel.app/32 or `openssl rand -base64 32`
AUTH_SECRET=XXXXXXXX
# Create a GitHub OAuth app here: https://github.com/settings/applications/new
# For info on authorization callback URL visit here: https://authjs.dev/reference/core/providers_github#callback-url
AUTH_GITHUB_ID=XXXXXXXX
AUTH_GITHUB_SECRET=XXXXXXXX
# Support OAuth login on preview deployments, see: https://authjs.dev/guides/basics/deployment#securing-a-preview-deployment
# Set the following only when deployed. In this example, we can reuse the same OAuth app, but if you are storing users, we recommend using a different OAuth app for development/production so that you don't mix your test and production user base.
# AUTH_REDIRECT_PROXY_URL=https://YOURAPP.vercel.app/api/auth

# Instructions to create kv database here: https://vercel.com/docs/storage/vercel-kv/quickstart and
KV_URL=XXXXXXXX
KV_REST_API_URL=XXXXXXXX
KV_REST_API_TOKEN=XXXXXXXX
KV_REST_API_READ_ONLY_TOKEN=XXXXXXXX

KV_REST_API_READ_ONLY_TOKEN=XXXXXXXX
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ This template ships with OpenAI `gpt-3.5-turbo` as the default. However, thanks

You can deploy your own version of the Next.js AI Chatbot to Vercel with one click:

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?demo-title=Next.js+Chat&demo-description=A+full-featured%2C+hackable+Next.js+AI+chatbot+built+by+Vercel+Labs&demo-url=https%3A%2F%2Fchat.vercel.ai%2F&demo-image=%2F%2Fimages.ctfassets.net%2Fe5382hct74si%2F4aVPvWuTmBvzM5cEdRdqeW%2F4234f9baf160f68ffb385a43c3527645%2FCleanShot_2023-06-16_at_17.09.21.png&project-name=Next.js+Chat&repository-name=nextjs-chat&repository-url=https%3A%2F%2Fgithub.com%2Fvercel-labs%2Fai-chatbot&from=templates&skippable-integrations=1&env=OPENAI_API_KEY%2CAUTH_GITHUB_ID%2CAUTH_GITHUB_SECRET%2CAUTH_SECRET&envDescription=How+to+get+these+env+vars&envLink=https%3A%2F%2Fgithub.com%2Fvercel-labs%2Fai-chatbot%2Fblob%2Fmain%2F.env.example&teamCreateStatus=hidden&stores=[{"type":"kv"}])
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?demo-title=Next.js+Chat&demo-description=A+full-featured%2C+hackable+Next.js+AI+chatbot+built+by+Vercel+Labs&demo-url=https%3A%2F%2Fchat.vercel.ai%2F&demo-image=%2F%2Fimages.ctfassets.net%2Fe5382hct74si%2F4aVPvWuTmBvzM5cEdRdqeW%2F4234f9baf160f68ffb385a43c3527645%2FCleanShot_2023-06-16_at_17.09.21.png&project-name=Next.js+Chat&repository-name=nextjs-chat&repository-url=https%3A%2F%2Fgithub.com%2Fvercel-labs%2Fai-chatbot&from=templates&skippable-integrations=1&env=OPENAI_API_KEY%2CAUTH_SECRET&envDescription=How+to+get+these+env+vars&envLink=https%3A%2F%2Fgithub.com%2Fvercel-labs%2Fai-chatbot%2Fblob%2Fmain%2F.env.example&teamCreateStatus=hidden&stores=[{"type":"kv"}])

## Creating a KV Database Instance

Expand Down
25 changes: 19 additions & 6 deletions app/(chat)/chat/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import { type Metadata } from 'next'
import { notFound, redirect } from 'next/navigation'

import { auth } from '@/auth'
import { getChat } from '@/app/actions'
import { getChat, getMissingKeys } from '@/app/actions'
import { Chat } from '@/components/chat'
import { AI } from '@/lib/chat/actions'
import { Session } from '@/lib/types'

export interface ChatPageProps {
params: {
Expand All @@ -27,21 +29,32 @@ export async function generateMetadata({
}

export default async function ChatPage({ params }: ChatPageProps) {
const session = await auth()
const session = (await auth()) as Session
const missingKeys = await getMissingKeys()

if (!session?.user) {
redirect(`/sign-in?next=/chat/${params.id}`)
redirect(`/login?next=/chat/${params.id}`)
}

const chat = await getChat(params.id, session.user.id)
const userId = session.user.id as string
const chat = await getChat(params.id, userId)

if (!chat) {
notFound()
redirect('/')
}

if (chat?.userId !== session?.user?.id) {
notFound()
}

return <Chat id={chat.id} initialMessages={chat.messages} />
return (
<AI initialAIState={{ chatId: chat.id, messages: chat.messages }}>
<Chat
id={chat.id}
session={session}
initialMessages={chat.messages}
missingKeys={missingKeys}
/>
</AI>
)
}
4 changes: 1 addition & 3 deletions app/(chat)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ export default async function ChatLayout({ children }: ChatLayoutProps) {
return (
<div className="relative flex h-[calc(100vh_-_theme(spacing.16))] overflow-hidden">
<SidebarDesktop />
<div className="group w-full overflow-auto pl-0 animate-in duration-300 ease-in-out peer-[[data-state=open]]:lg:pl-[250px] peer-[[data-state=open]]:xl:pl-[300px]">
{children}
</div>
{children}
</div>
)
}
18 changes: 16 additions & 2 deletions app/(chat)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
import { nanoid } from '@/lib/utils'
import { Chat } from '@/components/chat'
import { AI } from '@/lib/chat/actions'
import { auth } from '@/auth'
import { Session } from '@/lib/types'
import { getMissingKeys } from '../actions'

export default function IndexPage() {
export const metadata = {
title: 'Next.js AI Chatbot'
}

export default async function IndexPage() {
const id = nanoid()
const session = (await auth()) as Session
const missingKeys = await getMissingKeys()

return <Chat id={id} />
return (
<AI initialAIState={{ chatId: id, messages: [] }}>
<Chat id={id} session={session} missingKeys={missingKeys} />
</AI>
)
}
30 changes: 29 additions & 1 deletion app/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ export async function removeChat({ id, path }: { id: string; path: string }) {
}
}

const uid = await kv.hget<string>(`chat:${id}`, 'userId')
//Convert uid to string for consistent comparison with session.user.id
const uid = String(await kv.hget(`chat:${id}`, 'userId'))

if (uid !== session?.user?.id) {
return {
Expand Down Expand Up @@ -126,3 +127,30 @@ export async function shareChat(id: string) {

return payload
}

export async function saveChat(chat: Chat) {
const session = await auth()

if (session && session.user) {
const pipeline = kv.pipeline()
pipeline.hmset(`chat:${chat.id}`, chat)
pipeline.zadd(`user:chat:${chat.userId}`, {
score: Date.now(),
member: `chat:${chat.id}`
})
await pipeline.exec()
} else {
return
}
}

export async function refreshHistory(path: string) {
redirect(path)
}

export async function getMissingKeys() {
const keysRequired = ['OPENAI_API_KEY']
return keysRequired
.map(key => (process.env[key] ? '' : key))
.filter(key => key !== '')
}
2 changes: 0 additions & 2 deletions app/api/auth/[...nextauth]/route.ts

This file was deleted.

65 changes: 0 additions & 65 deletions app/api/chat/route.ts

This file was deleted.

74 changes: 36 additions & 38 deletions app/globals.css
Original file line number Diff line number Diff line change
@@ -1,78 +1,76 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
:root {
--background: 0 0% 100%;
--foreground: 240 10% 3.9%;

--muted: 240 4.8% 95.9%;
--muted-foreground: 240 3.8% 46.1%;

--popover: 0 0% 100%;
--popover-foreground: 240 10% 3.9%;

--card: 0 0% 100%;
--card-foreground: 240 10% 3.9%;

--border: 240 5.9% 90%;
--input: 240 5.9% 90%;

--popover: 0 0% 100%;
--popover-foreground: 240 10% 3.9%;
--primary: 240 5.9% 10%;
--primary-foreground: 0 0% 98%;

--secondary: 240 4.8% 95.9%;
--secondary-foreground: 240 5.9% 10%;


--muted: 240 4.8% 95.9%;
--muted-foreground: 240 3.8% 46.1%;

--accent: 240 4.8% 95.9%;
--accent-foreground: ;

--accent-foreground: 240 5.9% 10%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;

--ring: 240 5% 64.9%;

--border: 240 5.9% 90%;
--input: 240 5.9% 90%;
--ring: 240 10% 3.9%;

--radius: 0.5rem;
}

.dark {
--background: 240 10% 3.9%;
--foreground: 0 0% 98%;

--muted: 240 3.7% 15.9%;
--muted-foreground: 240 5% 64.9%;

--popover: 240 10% 3.9%;
--popover-foreground: 0 0% 98%;


--card: 240 10% 3.9%;
--card-foreground: 0 0% 98%;

--border: 240 3.7% 15.9%;
--input: 240 3.7% 15.9%;

--popover: 240 10% 3.9%;
--popover-foreground: 0 0% 98%;
--primary: 0 0% 98%;
--primary-foreground: 240 5.9% 10%;

--secondary: 240 3.7% 15.9%;
--secondary-foreground: 0 0% 98%;


--muted: 240 3.7% 15.9%;
--muted-foreground: 240 5% 64.9%;

--accent: 240 3.7% 15.9%;
--accent-foreground: ;

--accent-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 85.7% 97.3%;

--ring: 240 3.7% 15.9%;
--destructive-foreground: 0 0% 98%;

--border: 240 3.7% 15.9%;
--input: 240 3.7% 15.9%;
--ring: 240 4.9% 83.9%;
}
}

@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}
}
8 changes: 5 additions & 3 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { Toaster } from 'react-hot-toast'
import { GeistSans } from 'geist/font/sans'
import { GeistMono } from 'geist/font/mono'

Expand All @@ -7,9 +6,12 @@ import { cn } from '@/lib/utils'
import { TailwindIndicator } from '@/components/tailwind-indicator'
import { Providers } from '@/components/providers'
import { Header } from '@/components/header'
import { Toaster } from '@/components/ui/sonner'

export const metadata = {
metadataBase: new URL(`https://${process.env.VERCEL_URL}`),
metadataBase: process.env.VERCEL_URL
? new URL(`https://${process.env.VERCEL_URL}`)
: undefined,
title: {
default: 'Next.js AI Chatbot',
template: `%s - Next.js AI Chatbot`
Expand Down Expand Up @@ -43,7 +45,7 @@ export default function RootLayout({ children }: RootLayoutProps) {
GeistMono.variable
)}
>
<Toaster />
<Toaster position="top-center" />
<Providers
attribute="class"
defaultTheme="system"
Expand Down
Loading