Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Supabaseify #1

Merged
merged 5 commits into from
Jul 7, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
17 changes: 9 additions & 8 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@
## Then get your OpenAI API Key here: https://platform.openai.com/account/api-keys
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://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app
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
AUTH_REDIRECT_PROXY_URL=https://auth.example.com/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

# Update these with your Supabase details from your project settings > API
# https://app.supabase.com/project/_/settings/api
# In local dev you can get these by running `supabase status`.
NEXT_PUBLIC_SUPABASE_URL=your-project-url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key

## Follow GitHub Oauth setup steps from Supabase:
AUTH_GITHUB_ID=XXXXXXXX
AUTH_GITHUB_SECRET=XXXXXXXX
26 changes: 19 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,34 +28,45 @@
- [Radix UI](https://radix-ui.com) for headless component primitives
- Icons from [Phosphor Icons](https://phosphoricons.com)
- Chat History, rate limiting, and session storage with [Vercel KV](https://vercel.com/storage/kv)
- [Next Auth](https://github.com/nextauthjs/next-auth) for authentication
- [Supabase Auth](https://supabase.com/auth) for authentication

## Model Providers

This template ships with OpenAI `gpt-3.5-turbo` as the default. However, thanks to the [Vercel AI SDK](https://sdk.vercel.ai/docs), you can switch LLM providers to [Anthropic](https://anthropic.com), [Hugging Face](https://huggingface.co), or using [LangChain](https://js.langchain.com) with just a few lines of code.

## Deploy Your Own
<!-- ## Deploy Your Own

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&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"}])
TODO: update button with supabase integration
[![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&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

Follow the steps outlined in the [quick start guide](https://vercel.com/docs/storage/vercel-kv/quickstart#create-a-kv-database) provided by Vercel. This guide will assist you in creating and configuring your KV database instance on Vercel, enabling your application to interact with it.

Remember to update your environment variables (`KV_URL`, `KV_REST_API_URL`, `KV_REST_API_TOKEN`, `KV_REST_API_READ_ONLY_TOKEN`) in the `.env` file with the appropriate credentials provided during the KV database setup.


## Running locally

You will need to use the environment variables [defined in `.env.example`](.env.example) to run Next.js AI Chatbot. It's recommended you use [Vercel Environment Variables](https://vercel.com/docs/concepts/projects/environment-variables) for this, but a `.env` file is all that is necessary.

> Note: You should not commit your `.env` file or it will expose secrets that will allow others to control access to your various OpenAI and authentication provider accounts.

1. Install Vercel CLI: `npm i -g vercel`
2. Link local instance with Vercel and GitHub accounts (creates `.vercel` directory): `vercel link`
3. Download your environment variables: `vercel env pull`
Copy the `.env.example` file and populate the required env vars:

```bash
cp .env.example .env
```

[Install the Supabase CLI](https://supabase.com/docs/guides/cli) and start the local Supabase stack:

```bash
npm install supabase --save-dev
npx supabase start
```

Install the local dependencies and start dev mode:

```bash
pnpm install
Expand All @@ -71,3 +82,4 @@ This library is created by [Vercel](https://vercel.com) and [Next.js](https://ne
- Jared Palmer ([@jaredpalmer](https://twitter.com/jaredpalmer)) - [Vercel](https://vercel.com)
- Shu Ding ([@shuding\_](https://twitter.com/shuding_)) - [Vercel](https://vercel.com)
- shadcn ([@shadcn](https://twitter.com/shadcn)) - [Contractor](https://shadcn.com)
- Thor Schaeff ([@thorwebdev](https://twitter.com/thorwebdev)) - [Supabaseifier](https://thor.bio)
2 changes: 0 additions & 2 deletions app/api/auth/[...nextauth]/route.ts

This file was deleted.

19 changes: 19 additions & 0 deletions app/api/auth/callback/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
import { cookies } from 'next/headers'
import { NextResponse } from 'next/server'

export async function GET(request: Request) {
// The `/auth/callback` route is required for the server-side auth flow implemented
// by the Auth Helpers package. It exchanges an auth code for the user's session.
// https://supabase.com/docs/guides/auth/auth-helpers/nextjs#managing-sign-in-with-code-exchange
const requestUrl = new URL(request.url)
const code = requestUrl.searchParams.get('code')

if (code) {
const supabase = createRouteHandlerClient({ cookies })
await supabase.auth.exchangeCodeForSession(code)
}

// URL to redirect to after sign in process completes
return NextResponse.redirect(requestUrl.origin)
}
41 changes: 8 additions & 33 deletions auth.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,10 @@
import NextAuth, { type DefaultSession } from 'next-auth'
import GitHub from 'next-auth/providers/github'
import { NextResponse } from 'next/server'
import { createServerActionClient } from '@supabase/auth-helpers-nextjs'
import { cookies } from 'next/headers'

declare module 'next-auth' {
interface Session {
user: {
/** The user's id. */
id: string
} & DefaultSession['user']
}
export const auth = async () => {
// Create a Supabase client configured to use cookies
const supabase = createServerActionClient({ cookies })
const { data, error } = await supabase.auth.getSession()
if (error) throw error
return data.session
}

export const {
handlers: { GET, POST },
auth,
CSRF_experimental
} = NextAuth({
providers: [GitHub],
callbacks: {
jwt({ token, profile }) {
if (profile) {
token.id = profile.id
token.image = profile.picture
}
return token
},
authorized({ auth }) {
return !!auth?.user
}
},
pages: {
signIn: '/sign-in'
}
Comment on lines -14 to -34
Copy link

Choose a reason for hiding this comment

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

just to call out a potential issue - these are actual config/UX choices https://github.com/vercel-labs/ai-chatbot/pull/90/files i think you have mostly addressed them (except for the forced signin?) but just proactively calling it out

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ah interesting, their deployed demo actually allows anonymous conversations without requiring you to log in. But yah, happy to add a middleware based redirect if user isn't logged in, is that your preference?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Okay, added here with an comment that it's optional: 842803c

})
2 changes: 1 addition & 1 deletion components/empty-screen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export function EmptyScreen({ setInput }: Pick<UseChatHelpers, 'setInput'>) {
<div className="mx-auto max-w-2xl px-4">
<div className="rounded-lg border bg-background p-8">
<h1 className="mb-2 text-lg font-semibold">
Welcome to Next.js AI Chatbot!
Welcome to the Supabasified Next.js AI Chatbot!
</h1>
<p className="mb-2 leading-normal text-muted-foreground">
This is an open source AI chatbot app template built with{' '}
Expand Down
7 changes: 3 additions & 4 deletions components/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import { SidebarFooter } from '@/components/sidebar-footer'
import { ThemeToggle } from '@/components/theme-toggle'
import { ClearHistory } from '@/components/clear-history'
import { UserMenu } from '@/components/user-menu'
import { LoginButton } from '@/components/login-button'

export async function Header() {
const session = await auth()
Expand Down Expand Up @@ -47,23 +46,23 @@ export async function Header() {
<UserMenu user={session.user} />
) : (
<Button variant="link" asChild className="-ml-2">
<Link href="/sign-in?callbackUrl=/">Login</Link>
<Link href="/sign-in">Login</Link>
</Button>
)}
</div>
</div>
<div className="flex items-center justify-end space-x-2">
<a
target="_blank"
href="https://github.com/vercel/nextjs-ai-chatbot/"
href="https://github.com/thorwebdev/vercel-ai-chatbot"
rel="noopener noreferrer"
className={cn(buttonVariants({ variant: 'outline' }))}
>
<IconGitHub />
<span className="hidden ml-2 md:flex">GitHub</span>
</a>
<a
href="https://github.com/vercel/nextjs-ai-chatbot/"
href="https://github.com/thorwebdev/vercel-ai-chatbot"
target="_blank"
className={cn(buttonVariants())}
>
Expand Down
12 changes: 8 additions & 4 deletions components/login-button.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client'

import * as React from 'react'
import { signIn } from 'next-auth/react'
import { createClientComponentClient } from '@supabase/auth-helpers-nextjs'

import { cn } from '@/lib/utils'
import { Button, type ButtonProps } from '@/components/ui/button'
Expand All @@ -19,13 +19,17 @@ export function LoginButton({
...props
}: LoginButtonProps) {
const [isLoading, setIsLoading] = React.useState(false)
// Create a Supabase client configured to use cookies
const supabase = createClientComponentClient()
return (
<Button
variant="outline"
onClick={() => {
onClick={async () => {
setIsLoading(true)
// next-auth signIn() function doesn't work yet at Edge Runtime due to usage of BroadcastChannel
signIn('github', { callbackUrl: `/` })
await supabase.auth.signInWithOAuth({
provider: 'github',
options: { redirectTo: `${location.origin}/api/auth/callback` }
})
}}
disabled={isLoading}
className={cn(className)}
Expand Down
46 changes: 30 additions & 16 deletions components/user-menu.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
'use client'

import Image from 'next/image'
import { type Session } from 'next-auth'
import { signOut } from 'next-auth/react'
import { type Session } from '@supabase/auth-helpers-nextjs'
import { createClientComponentClient } from '@supabase/auth-helpers-nextjs'
import { useRouter } from 'next/navigation'

import { Button } from '@/components/ui/button'
import {
Expand All @@ -24,28 +25,48 @@ function getUserInitials(name: string) {
}

export function UserMenu({ user }: UserMenuProps) {
const router = useRouter()

// Create a Supabase client configured to use cookies
const supabase = createClientComponentClient()

const signOut = async () => {
await supabase.auth.signOut()
router.refresh()
}

return (
<div className="flex items-center justify-between">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="pl-0">
{user?.image ? (
{user?.user_metadata.avatar_url ? (
<Image
height={60}
width={60}
className="w-6 h-6 transition-opacity duration-300 rounded-full select-none ring-1 ring-zinc-100/10 hover:opacity-80"
src={user?.image ? `${user.image}&s=60` : ''}
alt={user.name ?? 'Avatar'}
src={
user?.user_metadata.avatar_url
? `${user.user_metadata.avatar_url}&s=60`
: ''
}
alt={user.user_metadata.name ?? 'Avatar'}
/>
) : (
<div className="flex items-center justify-center text-xs font-medium uppercase rounded-full select-none h-7 w-7 shrink-0 bg-muted/50 text-muted-foreground">
{user?.name ? getUserInitials(user?.name) : null}
{user?.user_metadata.name
? getUserInitials(user?.user_metadata.name)
: null}
</div>
)}
<span className="ml-2">{user?.name}</span>
<span className="ml-2">{user?.user_metadata.name}</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent sideOffset={8} align="start" className="w-[180px]">
<DropdownMenuItem className="flex-col items-start">
<div className="text-xs font-medium">{user?.name}</div>
<div className="text-xs font-medium">
{user?.user_metadata.name}
</div>
<div className="text-xs text-zinc-500">{user?.email}</div>
</DropdownMenuItem>
<DropdownMenuSeparator />
Expand All @@ -60,14 +81,7 @@ export function UserMenu({ user }: UserMenuProps) {
<IconExternalLink className="w-3 h-3 ml-auto" />
</a>
</DropdownMenuItem>
<DropdownMenuItem
onClick={() =>
signOut({
callbackUrl: '/'
})
}
className="text-xs"
>
<DropdownMenuItem onClick={signOut} className="text-xs">
Log Out
</DropdownMenuItem>
</DropdownMenuContent>
Expand Down
18 changes: 15 additions & 3 deletions middleware.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
export { auth as middleware } from './auth'
import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs'
import { NextResponse } from 'next/server'

export const config = {
matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)']
import type { NextRequest } from 'next/server'

export async function middleware(req: NextRequest) {
const res = NextResponse.next()

// Create a Supabase client configured to use cookies
const supabase = createMiddlewareClient({ req, res })

// Refresh session if expired - required for Server Components
// https://supabase.com/docs/guides/auth/auth-helpers/nextjs#managing-session-with-middleware
await supabase.auth.getSession()

return res
}
12 changes: 10 additions & 2 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@
module.exports = {
reactStrictMode: true,
experimental: {
serverActions: true,
serverActions: true
},
};
images: {
remotePatterns: [
{
protocol: 'https',
hostname: '**.githubusercontent.com'
}
]
}
}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-switch": "^1.0.3",
"@radix-ui/react-tooltip": "^1.0.6",
"@supabase/auth-helpers-nextjs": "^0.7.2",
"@supabase/supabase-js": "^2.26.0",
"@vercel/analytics": "^1.0.0",
"@vercel/kv": "^0.2.1",
"@vercel/og": "^0.5.7",
Expand All @@ -32,7 +34,6 @@
"focus-trap-react": "^10.1.1",
"nanoid": "^4.0.2",
"next": "13.4.7-canary.1",
"next-auth": "0.0.0-manual.83c4ebd1",
"next-themes": "^0.2.1",
"openai-edge": "^0.5.1",
"react": "^18.2.0",
Expand Down
Loading