diff --git a/.env.example b/.env.example index a703b1098..6022734a9 100644 --- a/.env.example +++ b/.env.example @@ -2,17 +2,12 @@ ## 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 +# 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 -## 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 - diff --git a/README.md b/README.md index 1105e31c6..acaa2bf18 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@

- An open-source AI chatbot app template built with Next.js, the Vercel AI SDK, OpenAI, and Vercel KV. + An open-source AI chatbot app template built with Next.js, the Vercel AI SDK, OpenAI, and Supabase Auth and Postgres DB.

@@ -27,35 +27,40 @@ - Styling with [Tailwind CSS](https://tailwindcss.com) - [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 +- Chat History with [Supabase Postgres DB](https://supabase.com) +- [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 + -## Creating a KV Database Instance +## Running locally -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. +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. -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. +> 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. +Copy the `.env.example` file and populate the required env vars: -## Running locally +```bash +cp .env.example .env +``` -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. +[Install the Supabase CLI](https://supabase.com/docs/guides/cli) and start the local Supabase stack: -> 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. +```bash +npm install supabase --save-dev +npx supabase start +``` -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` +Install the local dependencies and start dev mode: ```bash pnpm install @@ -71,3 +76,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) diff --git a/app/actions.ts b/app/actions.ts index 2c8a5ddf9..db0aa5667 100644 --- a/app/actions.ts +++ b/app/actions.ts @@ -1,11 +1,15 @@ 'use server' +import { createServerActionClient } from '@supabase/auth-helpers-nextjs' +import { cookies } from 'next/headers' +import { Database } from '@/lib/db_types' import { revalidatePath } from 'next/cache' import { redirect } from 'next/navigation' -import { kv } from '@vercel/kv' -import { auth } from '@/auth' import { type Chat } from '@/lib/types' +import { auth } from '@/auth' + +const supabase = createServerActionClient({ cookies }) export async function getChats(userId?: string | null) { if (!userId) { @@ -13,108 +17,81 @@ export async function getChats(userId?: string | null) { } try { - const pipeline = kv.pipeline() - const chats: string[] = await kv.zrange(`user:chat:${userId}`, 0, -1, { - rev: true - }) - - for (const chat of chats) { - pipeline.hgetall(chat) - } + const { data } = await supabase + .from('chats') + .select('payload') + .order('payload->createdAt', { ascending: false }) + .throwOnError() - const results = await pipeline.exec() - - return results as Chat[] + return (data?.map(entry => entry.payload) as Chat[]) ?? [] } catch (error) { return [] } } -export async function getChat(id: string, userId: string) { - const chat = await kv.hgetall(`chat:${id}`) - - if (!chat || (userId && chat.userId !== userId)) { - return null - } +export async function getChat(id: string) { + const { data } = await supabase + .from('chats') + .select('payload') + .eq('id', id) + .maybeSingle() - return chat + return (data?.payload as Chat) ?? null } export async function removeChat({ id, path }: { id: string; path: string }) { - const session = await auth() - - if (!session) { - return { - error: 'Unauthorized' - } - } - - const uid = await kv.hget(`chat:${id}`, 'userId') + try { + await supabase.from('chats').delete().eq('id', id).throwOnError() - if (uid !== session?.user?.id) { + revalidatePath('/') + return revalidatePath(path) + } catch (error) { return { error: 'Unauthorized' } } - - await kv.del(`chat:${id}`) - await kv.zrem(`user:chat:${session.user.id}`, `chat:${id}`) - - revalidatePath('/') - return revalidatePath(path) } export async function clearChats() { - const session = await auth() - - if (!session?.user?.id) { + try { + const session = await auth() + await supabase + .from('chats') + .delete() + .eq('user_id', session?.user.id) + .throwOnError() + revalidatePath('/') + return redirect('/') + } catch (error) { + console.log('clear chats error', error) return { error: 'Unauthorized' } } - - const chats: string[] = await kv.zrange(`user:chat:${session.user.id}`, 0, -1) - if (!chats.length) { - return redirect('/') - } - const pipeline = kv.pipeline() - - for (const chat of chats) { - pipeline.del(chat) - pipeline.zrem(`user:chat:${session.user.id}`, chat) - } - - await pipeline.exec() - - revalidatePath('/') - return redirect('/') } export async function getSharedChat(id: string) { - const chat = await kv.hgetall(`chat:${id}`) - - if (!chat || !chat.sharePath) { - return null - } - - return chat + const { data } = await supabase + .from('chats') + .select('payload') + .eq('id', id) + .not('payload->sharePath', 'is', null) + .maybeSingle() + + return (data?.payload as Chat) ?? null } export async function shareChat(chat: Chat) { - const session = await auth() - - if (!session?.user?.id || session.user.id !== chat.userId) { - return { - error: 'Unauthorized' - } - } - const payload = { ...chat, sharePath: `/share/${chat.id}` } - await kv.hmset(`chat:${chat.id}`, payload) + await supabase + .from('chats') + .update({ payload: payload as any }) + .eq('id', chat.id) + .throwOnError() return payload } diff --git a/app/api/auth/[...nextauth]/route.ts b/app/api/auth/[...nextauth]/route.ts deleted file mode 100644 index 883210bb2..000000000 --- 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/auth/callback/route.ts b/app/api/auth/callback/route.ts new file mode 100644 index 000000000..dbb00609f --- /dev/null +++ b/app/api/auth/callback/route.ts @@ -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) +} diff --git a/app/api/chat/route.ts b/app/api/chat/route.ts index 02e7571db..ae6c653a4 100644 --- a/app/api/chat/route.ts +++ b/app/api/chat/route.ts @@ -1,6 +1,8 @@ -import { kv } from '@vercel/kv' import { OpenAIStream, StreamingTextResponse } from 'ai' import { Configuration, OpenAIApi } from 'openai-edge' +import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs' +import { cookies } from 'next/headers' +import { Database } from '@/lib/db_types' import { auth } from '@/auth' import { nanoid } from '@/lib/utils' @@ -14,6 +16,7 @@ const configuration = new Configuration({ const openai = new OpenAIApi(configuration) export async function POST(req: Request) { + const supabase = createRouteHandlerClient({ cookies }) const json = await req.json() const { messages, previewToken } = json const userId = (await auth())?.user.id @@ -55,11 +58,8 @@ export async function POST(req: Request) { } ] } - await kv.hmset(`chat:${id}`, payload) - await kv.zadd(`user:chat:${userId}`, { - score: createdAt, - member: `chat:${id}` - }) + // Insert chat into database. + await supabase.from('chats').upsert({ id, payload }).throwOnError() } }) diff --git a/app/chat/[id]/page.tsx b/app/chat/[id]/page.tsx index 25d6c3063..01d2d3d4d 100644 --- a/app/chat/[id]/page.tsx +++ b/app/chat/[id]/page.tsx @@ -23,7 +23,7 @@ export async function generateMetadata({ return {} } - const chat = await getChat(params.id, session.user.id) + const chat = await getChat(params.id) return { title: chat?.title.toString().slice(0, 50) ?? 'Chat' } @@ -36,7 +36,7 @@ export default async function ChatPage({ params }: ChatPageProps) { redirect(`/sign-in?next=/chat/${params.id}`) } - const chat = await getChat(params.id, session.user.id) + const chat = await getChat(params.id) if (!chat) { notFound() diff --git a/app/share/[id]/opengraph-image.tsx b/app/share/[id]/opengraph-image.tsx index aefc610ed..bef39d93f 100644 --- a/app/share/[id]/opengraph-image.tsx +++ b/app/share/[id]/opengraph-image.tsx @@ -90,7 +90,7 @@ export default async function Image({ params }: ImageProps) {

Built with{' '}
Vercel AI SDK
& -
KV
+
Supabase Auth & DB
chat.vercel.ai
diff --git a/auth.ts b/auth.ts index d272d78db..49cc10a6d 100644 --- a/auth.ts +++ b/auth.ts @@ -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' - } -}) diff --git a/components/empty-screen.tsx b/components/empty-screen.tsx index 90859309b..ec00b7827 100644 --- a/components/empty-screen.tsx +++ b/components/empty-screen.tsx @@ -24,15 +24,12 @@ export function EmptyScreen({ setInput }: Pick) {

- Welcome to Next.js AI Chatbot! + Welcome to the Supabaseified Next.js AI Chatbot!

This is an open source AI chatbot app template built with{' '} Next.js and{' '} - - Vercel KV - - . + Supabase.

You can start a conversation here or try the following examples: diff --git a/components/footer.tsx b/components/footer.tsx index 0a8fce3c2..8d96068a0 100644 --- a/components/footer.tsx +++ b/components/footer.tsx @@ -14,10 +14,7 @@ export function FooterText({ className, ...props }: React.ComponentProps<'p'>) { > Open source AI chatbot built with{' '} Next.js and{' '} - - Vercel KV - - . + Supabase.

) } diff --git a/components/header.tsx b/components/header.tsx index 3ff0dd6fc..050ef72c9 100644 --- a/components/header.tsx +++ b/components/header.tsx @@ -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() @@ -47,7 +46,7 @@ export async function Header() { ) : ( )}
@@ -55,7 +54,7 @@ export async function Header() {
@@ -63,7 +62,7 @@ export async function Header() { GitHub diff --git a/components/login-button.tsx b/components/login-button.tsx index ae8f84274..0ae40bab2 100644 --- a/components/login-button.tsx +++ b/components/login-button.tsx @@ -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' @@ -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 ( -
{user?.name}
+
+ {user?.user_metadata.name} +
{user?.email}
@@ -60,14 +81,7 @@ export function UserMenu({ user }: UserMenuProps) {
- - signOut({ - callbackUrl: '/' - }) - } - className="text-xs" - > + Log Out diff --git a/lib/db_types.ts b/lib/db_types.ts new file mode 100644 index 000000000..dda809f61 --- /dev/null +++ b/lib/db_types.ts @@ -0,0 +1,256 @@ +export type Json = + | string + | number + | boolean + | null + | { [key: string]: Json | undefined } + | Json[] + +export interface Database { + graphql_public: { + Tables: { + [_ in never]: never + } + Views: { + [_ in never]: never + } + Functions: { + graphql: { + Args: { + operationName?: string + query?: string + variables?: Json + extensions?: Json + } + Returns: Json + } + } + Enums: { + [_ in never]: never + } + CompositeTypes: { + [_ in never]: never + } + } + public: { + Tables: { + chats: { + Row: { + id: string + payload: Json | null + user_id: string | null + } + Insert: { + id: string + payload?: Json | null + user_id?: string | null + } + Update: { + id?: string + payload?: Json | null + user_id?: string | null + } + Relationships: [ + { + foreignKeyName: "chats_user_id_fkey" + columns: ["user_id"] + referencedRelation: "users" + referencedColumns: ["id"] + } + ] + } + } + Views: { + [_ in never]: never + } + Functions: { + [_ in never]: never + } + Enums: { + [_ in never]: never + } + CompositeTypes: { + [_ in never]: never + } + } + storage: { + Tables: { + buckets: { + Row: { + allowed_mime_types: string[] | null + avif_autodetection: boolean | null + created_at: string | null + file_size_limit: number | null + id: string + name: string + owner: string | null + public: boolean | null + updated_at: string | null + } + Insert: { + allowed_mime_types?: string[] | null + avif_autodetection?: boolean | null + created_at?: string | null + file_size_limit?: number | null + id: string + name: string + owner?: string | null + public?: boolean | null + updated_at?: string | null + } + Update: { + allowed_mime_types?: string[] | null + avif_autodetection?: boolean | null + created_at?: string | null + file_size_limit?: number | null + id?: string + name?: string + owner?: string | null + public?: boolean | null + updated_at?: string | null + } + Relationships: [ + { + foreignKeyName: "buckets_owner_fkey" + columns: ["owner"] + referencedRelation: "users" + referencedColumns: ["id"] + } + ] + } + migrations: { + Row: { + executed_at: string | null + hash: string + id: number + name: string + } + Insert: { + executed_at?: string | null + hash: string + id: number + name: string + } + Update: { + executed_at?: string | null + hash?: string + id?: number + name?: string + } + Relationships: [] + } + objects: { + Row: { + bucket_id: string | null + created_at: string | null + id: string + last_accessed_at: string | null + metadata: Json | null + name: string | null + owner: string | null + path_tokens: string[] | null + updated_at: string | null + version: string | null + } + Insert: { + bucket_id?: string | null + created_at?: string | null + id?: string + last_accessed_at?: string | null + metadata?: Json | null + name?: string | null + owner?: string | null + path_tokens?: string[] | null + updated_at?: string | null + version?: string | null + } + Update: { + bucket_id?: string | null + created_at?: string | null + id?: string + last_accessed_at?: string | null + metadata?: Json | null + name?: string | null + owner?: string | null + path_tokens?: string[] | null + updated_at?: string | null + version?: string | null + } + Relationships: [ + { + foreignKeyName: "objects_bucketId_fkey" + columns: ["bucket_id"] + referencedRelation: "buckets" + referencedColumns: ["id"] + } + ] + } + } + Views: { + [_ in never]: never + } + Functions: { + can_insert_object: { + Args: { + bucketid: string + name: string + owner: string + metadata: Json + } + Returns: undefined + } + extension: { + Args: { + name: string + } + Returns: string + } + filename: { + Args: { + name: string + } + Returns: string + } + foldername: { + Args: { + name: string + } + Returns: unknown + } + get_size_by_bucket: { + Args: Record + Returns: { + size: number + bucket_id: string + }[] + } + search: { + Args: { + prefix: string + bucketname: string + limits?: number + levels?: number + offsets?: number + search?: string + sortcolumn?: string + sortorder?: string + } + Returns: { + name: string + id: string + updated_at: string + created_at: string + last_accessed_at: string + metadata: Json + }[] + } + } + Enums: { + [_ in never]: never + } + CompositeTypes: { + [_ in never]: never + } + } +} + diff --git a/lib/types.ts b/lib/types.ts index 6f86a9cd1..4b75a98a4 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -1,5 +1,6 @@ import { type Message } from 'ai' +// TODO refactor and remove unneccessary duplicate data. export interface Chat extends Record { id: string title: string @@ -7,7 +8,7 @@ export interface Chat extends Record { userId: string path: string messages: Message[] - sharePath?: string + sharePath?: string // Refactor to use RLS } export type ServerActionResult = Promise< diff --git a/middleware.ts b/middleware.ts index 83a5d0843..84068d187 100644 --- a/middleware.ts +++ b/middleware.ts @@ -1,5 +1,42 @@ -export { auth as middleware } from './auth' +import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs' +import { NextResponse } from 'next/server' + +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 + const { + data: { session } + } = await supabase.auth.getSession() + + // OPTIONAL: this forces users to be logged in to use the chatbot. + // If you want to allow anonymous users, simply remove the check below. + if (!session && !req.url.includes('/sign-in')) { + const redirectUrl = req.nextUrl.clone() + redirectUrl.pathname = '/sign-in' + redirectUrl.searchParams.set(`redirectedFrom`, req.nextUrl.pathname) + return NextResponse.redirect(redirectUrl) + } + + return res +} export const config = { - matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'] + matcher: [ + /* + * Match all request paths except for the ones starting with: + * - share (publicly shared chats) + * - api (API routes) + * - _next/static (static files) + * - _next/image (image optimization files) + * - favicon.ico (favicon file) + */ + '/((?!share|api|_next/static|_next/image|favicon.ico).*)' + ] } diff --git a/next.config.js b/next.config.js index 11854b3ab..1eed4d931 100644 --- a/next.config.js +++ b/next.config.js @@ -2,6 +2,14 @@ module.exports = { reactStrictMode: true, experimental: { - serverActions: true, + serverActions: true }, -}; + images: { + remotePatterns: [ + { + protocol: 'https', + hostname: '**.githubusercontent.com' + } + ] + } +} diff --git a/package.json b/package.json index 2cad86033..65a468ab4 100644 --- a/package.json +++ b/package.json @@ -23,8 +23,9 @@ "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-switch": "^1.0.3", "@radix-ui/react-tooltip": "^1.0.6", - "@vercel/analytics": "^1.0.0", - "@vercel/kv": "^0.2.1", + "@supabase/auth-helpers-nextjs": "^0.7.2", + "@supabase/supabase-js": "^2.26.0", + "@vercel/analytics": "^1.0.1", "@vercel/og": "^0.5.7", "ai": "^2.1.6", "class-variance-authority": "^0.4.0", @@ -32,7 +33,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", @@ -47,20 +47,20 @@ }, "devDependencies": { "@tailwindcss/typography": "^0.5.9", - "@types/node": "^17.0.12", - "@types/react": "^18.0.22", - "@types/react-dom": "^18.0.7", + "@types/node": "^17.0.45", + "@types/react": "^18.2.6", + "@types/react-dom": "^18.2.4", "@types/react-syntax-highlighter": "^15.5.6", "@typescript-eslint/parser": "^5.59.7", - "autoprefixer": "^10.4.13", - "eslint": "^8.31.0", + "autoprefixer": "^10.4.14", + "eslint": "^8.40.0", "eslint-config-next": "13.4.7-canary.1", - "eslint-config-prettier": "^8.3.0", + "eslint-config-prettier": "^8.8.0", "eslint-plugin-tailwindcss": "^3.12.0", - "postcss": "^8.4.21", - "prettier": "^2.7.1", + "postcss": "^8.4.23", + "prettier": "^2.8.8", "tailwind-merge": "^1.12.0", - "tailwindcss": "^3.3.1", + "tailwindcss": "^3.3.2", "tailwindcss-animate": "^1.0.5", "typescript": "^5.1.3" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2ca8d29a6..dc66fe433 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,12 +32,15 @@ dependencies: '@radix-ui/react-tooltip': specifier: ^1.0.6 version: 1.0.6(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0) + '@supabase/auth-helpers-nextjs': + specifier: ^0.7.2 + version: 0.7.2(@supabase/supabase-js@2.26.0) + '@supabase/supabase-js': + specifier: ^2.26.0 + version: 2.26.0 '@vercel/analytics': - specifier: ^1.0.0 + specifier: ^1.0.1 version: 1.0.1 - '@vercel/kv': - specifier: ^0.2.1 - version: 0.2.1 '@vercel/og': specifier: ^0.5.7 version: 0.5.7 @@ -59,9 +62,6 @@ dependencies: next: specifier: 13.4.7-canary.1 version: 13.4.7-canary.1(react-dom@18.2.0)(react@18.2.0) - next-auth: - specifier: 0.0.0-manual.83c4ebd1 - version: 0.0.0-manual.83c4ebd1(next@13.4.7-canary.1)(react@18.2.0) next-themes: specifier: ^0.2.1 version: 0.2.1(next@13.4.7-canary.1)(react-dom@18.2.0)(react@18.2.0) @@ -101,13 +101,13 @@ devDependencies: specifier: ^0.5.9 version: 0.5.9(tailwindcss@3.3.2) '@types/node': - specifier: ^17.0.12 + specifier: ^17.0.45 version: 17.0.45 '@types/react': - specifier: ^18.0.22 + specifier: ^18.2.6 version: 18.2.6 '@types/react-dom': - specifier: ^18.0.7 + specifier: ^18.2.4 version: 18.2.4 '@types/react-syntax-highlighter': specifier: ^15.5.6 @@ -116,31 +116,31 @@ devDependencies: specifier: ^5.59.7 version: 5.59.7(eslint@8.40.0)(typescript@5.1.3) autoprefixer: - specifier: ^10.4.13 + specifier: ^10.4.14 version: 10.4.14(postcss@8.4.23) eslint: - specifier: ^8.31.0 + specifier: ^8.40.0 version: 8.40.0 eslint-config-next: specifier: 13.4.7-canary.1 version: 13.4.7-canary.1(eslint@8.40.0)(typescript@5.1.3) eslint-config-prettier: - specifier: ^8.3.0 + specifier: ^8.8.0 version: 8.8.0(eslint@8.40.0) eslint-plugin-tailwindcss: specifier: ^3.12.0 version: 3.12.0(tailwindcss@3.3.2) postcss: - specifier: ^8.4.21 + specifier: ^8.4.23 version: 8.4.23 prettier: - specifier: ^2.7.1 + specifier: ^2.8.8 version: 2.8.8 tailwind-merge: specifier: ^1.12.0 version: 1.12.0 tailwindcss: - specifier: ^3.3.1 + specifier: ^3.3.2 version: 3.3.2 tailwindcss-animate: specifier: ^1.0.5 @@ -156,22 +156,6 @@ packages: engines: {node: '>=10'} dev: true - /@auth/core@0.0.0-manual.8fcd46b0: - resolution: {integrity: sha512-KuhvZ0hHz6NvMAgAi+su0dJOD0YAiOWGaLswyfGK5RsG/cdhqyyiII9HOTaZbWZAKir0UGYb8SlN+owhV30JXg==} - peerDependencies: - nodemailer: ^6.8.0 - peerDependenciesMeta: - nodemailer: - optional: true - dependencies: - '@panva/hkdf': 1.1.1 - cookie: 0.5.0 - jose: 4.14.4 - oauth4webapi: 2.3.0 - preact: 10.11.3 - preact-render-to-string: 5.2.3(preact@10.11.3) - dev: false - /@babel/helper-string-parser@7.22.5: resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==} engines: {node: '>=6.9.0'} @@ -182,8 +166,8 @@ packages: engines: {node: '>=6.9.0'} dev: false - /@babel/parser@7.22.5: - resolution: {integrity: sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q==} + /@babel/parser@7.22.7: + resolution: {integrity: sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q==} engines: {node: '>=6.0.0'} hasBin: true dependencies: @@ -428,10 +412,6 @@ packages: fastq: 1.15.0 dev: true - /@panva/hkdf@1.1.1: - resolution: {integrity: sha512-dhPeilub1NuIG0X5Kvhh9lH4iW3ZsHlnzwgwbOlgwQ2wG1IqFzsgHqmKPk3WzsdWAeaxKJxgM0+W433RmN45GA==} - dev: false - /@pkgr/utils@2.4.0: resolution: {integrity: sha512-2OCURAmRtdlL8iUDTypMrrxfwe8frXTeXaxGsVOaYtc/wrUyk8Z/0OBetM7cdlsy7ZFWlMX72VogKeh+A4Xcjw==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} @@ -1172,6 +1152,81 @@ packages: string.prototype.codepointat: 0.2.1 dev: false + /@supabase/auth-helpers-nextjs@0.7.2(@supabase/supabase-js@2.26.0): + resolution: {integrity: sha512-n5IyGBYJV/WiR5Rgw4CUaiJYiOv9yW2of4ZP4EyzKt2O6/6rztt7PcGE4AoK2vERw+fb5F2QtJBdt6J5eOYCCw==} + peerDependencies: + '@supabase/supabase-js': ^2.19.0 + dependencies: + '@supabase/auth-helpers-shared': 0.4.1(@supabase/supabase-js@2.26.0) + '@supabase/supabase-js': 2.26.0 + set-cookie-parser: 2.6.0 + dev: false + + /@supabase/auth-helpers-shared@0.4.1(@supabase/supabase-js@2.26.0): + resolution: {integrity: sha512-IEDX9JzWkIjQiLUaP4Qy5YDiG0jFQatWfS+jw8cCQs6QfbNdEPd2Y3qonwGHnM90CZom9SvjuylBv2pFVAL7Lw==} + peerDependencies: + '@supabase/supabase-js': ^2.19.0 + dependencies: + '@supabase/supabase-js': 2.26.0 + jose: 4.14.4 + dev: false + + /@supabase/functions-js@2.1.2: + resolution: {integrity: sha512-QCR6pwJs9exCl37bmpMisUd6mf+0SUBJ6mUpiAjEkSJ/+xW8TCuO14bvkWHADd5hElJK9MxNlMQXxSA4DRz9nQ==} + dependencies: + cross-fetch: 3.1.8 + transitivePeerDependencies: + - encoding + dev: false + + /@supabase/gotrue-js@2.39.0: + resolution: {integrity: sha512-Fhro0zcglqKAJRLeGGompySUwx74RM/CE1ICd/7R2qhpE7qWDiTM0f2s6+qK5YQnu7akHnIIvSn9NAN9MuTOqw==} + dependencies: + cross-fetch: 3.1.8 + transitivePeerDependencies: + - encoding + dev: false + + /@supabase/postgrest-js@1.7.2: + resolution: {integrity: sha512-GK80JpRq8l6Qll85erICypAfQCied8tdlXfsDN14W844HqXCSOisk8AaE01DAwGJanieaoN5fuqhzA2yKxDvEQ==} + dependencies: + cross-fetch: 3.1.8 + transitivePeerDependencies: + - encoding + dev: false + + /@supabase/realtime-js@2.7.3: + resolution: {integrity: sha512-c7TzL81sx2kqyxsxcDduJcHL9KJdCOoKimGP6lQSqiZKX42ATlBZpWbyy9KFGFBjAP4nyopMf5JhPi2ZH9jyNw==} + dependencies: + '@types/phoenix': 1.6.0 + '@types/websocket': 1.0.5 + websocket: 1.0.34 + transitivePeerDependencies: + - supports-color + dev: false + + /@supabase/storage-js@2.5.1: + resolution: {integrity: sha512-nkR0fQA9ScAtIKA3vNoPEqbZv1k5B5HVRYEvRWdlP6mUpFphM9TwPL2jZ/ztNGMTG5xT6SrHr+H7Ykz8qzbhjw==} + dependencies: + cross-fetch: 3.1.8 + transitivePeerDependencies: + - encoding + dev: false + + /@supabase/supabase-js@2.26.0: + resolution: {integrity: sha512-RXmTPTobaYAwkSobadHZmEVLmzX3SGrtRZIGfLWnLv92VzBRrjuXn0a+bJqKl50GUzsyqPA+j5pod7EwMkcH5A==} + dependencies: + '@supabase/functions-js': 2.1.2 + '@supabase/gotrue-js': 2.39.0 + '@supabase/postgrest-js': 1.7.2 + '@supabase/realtime-js': 2.7.3 + '@supabase/storage-js': 2.5.1 + cross-fetch: 3.1.8 + transitivePeerDependencies: + - encoding + - supports-color + dev: false + /@swc/helpers@0.5.1: resolution: {integrity: sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg==} dependencies: @@ -1222,7 +1277,10 @@ packages: /@types/node@17.0.45: resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==} - dev: true + + /@types/phoenix@1.6.0: + resolution: {integrity: sha512-qwfpsHmFuhAS/dVd4uBIraMxRd56vwBUYQGZ6GpXnFuM2XMRFJbIyruFKKlW2daQliuYZwe0qfn/UjFCDKic5g==} + dev: false /@types/prop-types@15.7.5: resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==} @@ -1252,6 +1310,12 @@ packages: resolution: {integrity: sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==} dev: false + /@types/websocket@1.0.5: + resolution: {integrity: sha512-NbsqiNX9CnEfC1Z0Vf4mE1SgAJ07JnRYcNex7AJ9zAVzmiGHmjKFEk7O4TJIsgv2B1sLEb6owKFZrACwdYngsQ==} + dependencies: + '@types/node': 17.0.45 + dev: false + /@typescript-eslint/parser@5.59.7(eslint@8.40.0)(typescript@5.1.3): resolution: {integrity: sha512-VhpsIEuq/8i5SF+mPg9jSdIwgMBBp0z9XqjiEay+81PYLJuroN+ET1hM5IhkiYMJd9MkTz8iJLt7aaGAgzWUbQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -1314,27 +1378,10 @@ packages: eslint-visitor-keys: 3.4.1 dev: true - /@upstash/redis@1.20.6: - resolution: {integrity: sha512-q1izaYEUsq/WiXNOjf4oOjFZe8fIeBSZN8d5cEyOD4nem+zxc4jccieorQQrNlEahKPE1ZYLzVEkMODRUfch2g==} - dependencies: - isomorphic-fetch: 3.0.0 - transitivePeerDependencies: - - encoding - dev: false - /@vercel/analytics@1.0.1: resolution: {integrity: sha512-Ux0c9qUfkcPqng3vrR0GTrlQdqNJ2JREn/2ydrVuKwM3RtMfF2mWX31Ijqo1opSjNAq6rK76PwtANw6kl6TAow==} dev: false - /@vercel/kv@0.2.1: - resolution: {integrity: sha512-0O1CVh0maG/bduAE6DPKUTfGSnORgrcS5xBYZCb62sOU7PrVZrXhaPbUSBE4q5PXS5DC+cpN6FY2RWNlslUaWQ==} - engines: {node: '>=14.6'} - dependencies: - '@upstash/redis': 1.20.6 - transitivePeerDependencies: - - encoding - dev: false - /@vercel/og@0.5.7: resolution: {integrity: sha512-KdhPHiLodj2n0Z0yg9feF/onMPlrimhBvCRJOwTpOjvNxefBqM1VIJW5+Ou9mFSIa37Gg0DGBDAoSOJLH2FqxA==} engines: {node: '>=16'} @@ -1347,7 +1394,7 @@ packages: /@vue/compiler-core@3.3.4: resolution: {integrity: sha512-cquyDNvZ6jTbf/+x+AgM2Arrp6G4Dzbb0R64jiG804HRMfRiFXWI6kqUVqZ6ZR0bQhIoQjB4+2bhNtVwndW15g==} dependencies: - '@babel/parser': 7.22.5 + '@babel/parser': 7.22.7 '@vue/shared': 3.3.4 estree-walker: 2.0.2 source-map-js: 1.0.2 @@ -1363,15 +1410,15 @@ packages: /@vue/compiler-sfc@3.3.4: resolution: {integrity: sha512-6y/d8uw+5TkCuzBkgLS0v3lSM3hJDntFEiUORM11pQ/hKvkhSKZrXW6i69UyXlJQisJxuUEJKAWEqWbWsLeNKQ==} dependencies: - '@babel/parser': 7.22.5 + '@babel/parser': 7.22.7 '@vue/compiler-core': 3.3.4 '@vue/compiler-dom': 3.3.4 '@vue/compiler-ssr': 3.3.4 '@vue/reactivity-transform': 3.3.4 '@vue/shared': 3.3.4 estree-walker: 2.0.2 - magic-string: 0.30.0 - postcss: 8.4.23 + magic-string: 0.30.1 + postcss: 8.4.25 source-map-js: 1.0.2 dev: false @@ -1385,11 +1432,11 @@ packages: /@vue/reactivity-transform@3.3.4: resolution: {integrity: sha512-MXgwjako4nu5WFLAjpBnCj/ieqcjE2aJBINUNQzkZQfzIZA4xn+0fV1tIYBJvvva3N3OvKGofRLvQIwEQPpaXw==} dependencies: - '@babel/parser': 7.22.5 + '@babel/parser': 7.22.7 '@vue/compiler-core': 3.3.4 '@vue/shared': 3.3.4 estree-walker: 2.0.2 - magic-string: 0.30.0 + magic-string: 0.30.1 dev: false /@vue/reactivity@3.3.4: @@ -1664,6 +1711,14 @@ packages: update-browserslist-db: 1.0.11(browserslist@4.21.5) dev: true + /bufferutil@4.0.7: + resolution: {integrity: sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==} + engines: {node: '>=6.14.2'} + requiresBuild: true + dependencies: + node-gyp-build: 4.6.0 + dev: false + /bundle-name@3.0.0: resolution: {integrity: sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==} engines: {node: '>=12'} @@ -1797,9 +1852,12 @@ packages: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} dev: true - /cookie@0.5.0: - resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} - engines: {node: '>= 0.6'} + /cross-fetch@3.1.8: + resolution: {integrity: sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==} + dependencies: + node-fetch: 2.6.12 + transitivePeerDependencies: + - encoding dev: false /cross-spawn@7.0.3: @@ -1841,10 +1899,28 @@ packages: /csstype@3.1.2: resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} + /d@1.0.1: + resolution: {integrity: sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==} + dependencies: + es5-ext: 0.10.62 + type: 1.2.0 + dev: false + /damerau-levenshtein@1.0.8: resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} dev: true + /debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.0.0 + dev: false + /debug@3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: @@ -2072,6 +2148,31 @@ packages: is-symbol: 1.0.4 dev: true + /es5-ext@0.10.62: + resolution: {integrity: sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==} + engines: {node: '>=0.10'} + requiresBuild: true + dependencies: + es6-iterator: 2.0.3 + es6-symbol: 3.1.3 + next-tick: 1.1.0 + dev: false + + /es6-iterator@2.0.3: + resolution: {integrity: sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==} + dependencies: + d: 1.0.1 + es5-ext: 0.10.62 + es6-symbol: 3.1.3 + dev: false + + /es6-symbol@3.1.3: + resolution: {integrity: sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==} + dependencies: + d: 1.0.1 + ext: 1.7.0 + dev: false + /escalade@3.1.1: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} engines: {node: '>=6'} @@ -2425,6 +2526,12 @@ packages: strip-final-newline: 3.0.0 dev: true + /ext@1.7.0: + resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==} + dependencies: + type: 2.7.2 + dev: false + /extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} dev: false @@ -3025,6 +3132,10 @@ packages: has-tostringtag: 1.0.0 dev: true + /is-typedarray@1.0.0: + resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} + dev: false + /is-weakmap@2.0.1: resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==} dev: true @@ -3057,15 +3168,6 @@ packages: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} dev: true - /isomorphic-fetch@3.0.0: - resolution: {integrity: sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==} - dependencies: - node-fetch: 2.6.11 - whatwg-fetch: 3.6.2 - transitivePeerDependencies: - - encoding - dev: false - /jiti@1.18.2: resolution: {integrity: sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg==} hasBin: true @@ -3201,8 +3303,8 @@ packages: yallist: 4.0.0 dev: true - /magic-string@0.30.0: - resolution: {integrity: sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==} + /magic-string@0.30.1: + resolution: {integrity: sha512-mbVKXPmS0z0G4XqFDCTllmDQ6coZzn94aMlb0o/A4HEHJCKcanlDZwYJgwnkmgD3jyWhUgj9VsPrfd972yPffA==} engines: {node: '>=12'} dependencies: '@jridgewell/sourcemap-codec': 1.4.15 @@ -3653,6 +3755,10 @@ packages: engines: {node: '>=4'} dev: false + /ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + dev: false + /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} @@ -3683,21 +3789,6 @@ packages: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true - /next-auth@0.0.0-manual.83c4ebd1(next@13.4.7-canary.1)(react@18.2.0): - resolution: {integrity: sha512-6nUXtVrZqTWvVz2NhUB/xbMyzxUSQycQWOquACwdpk63AcnZ3g01v+W0UX9paqkGIdqLPtI/pL6rA2N+MT58SQ==} - peerDependencies: - next: ^13.4.6 - nodemailer: ^6.6.5 - react: ^18.2.0 - peerDependenciesMeta: - nodemailer: - optional: true - dependencies: - '@auth/core': 0.0.0-manual.8fcd46b0 - next: 13.4.7-canary.1(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - dev: false - /next-themes@0.2.1(next@13.4.7-canary.1)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A==} peerDependencies: @@ -3710,6 +3801,10 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false + /next-tick@1.1.0: + resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} + dev: false + /next@13.4.7-canary.1(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-IKmDxqALqXSSJHSdlslKq1Dry5x8gaQfXtOo8acyVRu0GDIc/Az8Hv/TWUkRGZPA76yllZeEf16LIv3QLkvLMA==} engines: {node: '>=16.8.0'} @@ -3753,8 +3848,8 @@ packages: - babel-plugin-macros dev: false - /node-fetch@2.6.11: - resolution: {integrity: sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==} + /node-fetch@2.6.12: + resolution: {integrity: sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==} engines: {node: 4.x || >=6.0.0} peerDependencies: encoding: ^0.1.0 @@ -3765,6 +3860,11 @@ packages: whatwg-url: 5.0.0 dev: false + /node-gyp-build@4.6.0: + resolution: {integrity: sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==} + hasBin: true + dev: false + /node-releases@2.0.10: resolution: {integrity: sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==} dev: true @@ -3793,10 +3893,6 @@ packages: path-key: 4.0.0 dev: true - /oauth4webapi@2.3.0: - resolution: {integrity: sha512-JGkb5doGrwzVDuHwgrR4nHJayzN4h59VCed6EW8Tql6iHDfZIabCJvg6wtbn5q6pyB2hZruI3b77Nudvq7NmvA==} - dev: false - /object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -4088,18 +4184,15 @@ packages: nanoid: 3.3.6 picocolors: 1.0.0 source-map-js: 1.0.2 + dev: true - /preact-render-to-string@5.2.3(preact@10.11.3): - resolution: {integrity: sha512-aPDxUn5o3GhWdtJtW0svRC2SS/l8D9MAgo2+AWml+BhDImb27ALf04Q2d+AHqUUOc6RdSXFIBVa2gxzgMKgtZA==} - peerDependencies: - preact: '>=10' + /postcss@8.4.25: + resolution: {integrity: sha512-7taJ/8t2av0Z+sQEvNzCkpDynl0tX3uJMCODi6nT3PfASC7dYCWV9aQ+uiCf+KBD4SEFcu+GvJdGdwzQ6OSjCw==} + engines: {node: ^10 || ^12 || >=14} dependencies: - preact: 10.11.3 - pretty-format: 3.8.0 - dev: false - - /preact@10.11.3: - resolution: {integrity: sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==} + nanoid: 3.3.6 + picocolors: 1.0.0 + source-map-js: 1.0.2 dev: false /prelude-ls@1.2.1: @@ -4113,10 +4206,6 @@ packages: hasBin: true dev: true - /pretty-format@3.8.0: - resolution: {integrity: sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==} - dev: false - /prismjs@1.27.0: resolution: {integrity: sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==} engines: {node: '>=6'} @@ -4475,6 +4564,10 @@ packages: lru-cache: 6.0.0 dev: true + /set-cookie-parser@2.6.0: + resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} + dev: false + /shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -4839,6 +4932,14 @@ packages: engines: {node: '>=10'} dev: true + /type@1.2.0: + resolution: {integrity: sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==} + dev: false + + /type@2.7.2: + resolution: {integrity: sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==} + dev: false + /typed-array-length@1.0.4: resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} dependencies: @@ -4847,6 +4948,12 @@ packages: is-typed-array: 1.1.10 dev: true + /typedarray-to-buffer@3.1.5: + resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} + dependencies: + is-typedarray: 1.0.0 + dev: false + /typescript@5.1.3: resolution: {integrity: sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==} engines: {node: '>=14.17'} @@ -5013,6 +5120,14 @@ packages: react: 18.2.0 dev: false + /utf-8-validate@5.0.10: + resolution: {integrity: sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==} + engines: {node: '>=6.14.2'} + requiresBuild: true + dependencies: + node-gyp-build: 4.6.0 + dev: false + /util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: true @@ -5066,8 +5181,18 @@ packages: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} dev: false - /whatwg-fetch@3.6.2: - resolution: {integrity: sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==} + /websocket@1.0.34: + resolution: {integrity: sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==} + engines: {node: '>=4.0.0'} + dependencies: + bufferutil: 4.0.7 + debug: 2.6.9 + es5-ext: 0.10.62 + typedarray-to-buffer: 3.1.5 + utf-8-validate: 5.0.10 + yaeti: 0.0.6 + transitivePeerDependencies: + - supports-color dev: false /whatwg-url@5.0.0: @@ -5130,6 +5255,11 @@ packages: engines: {node: '>=0.4'} dev: false + /yaeti@0.0.6: + resolution: {integrity: sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==} + engines: {node: '>=0.10.32'} + dev: false + /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} dev: true diff --git a/supabase/.gitignore b/supabase/.gitignore new file mode 100644 index 000000000..773c7c3e0 --- /dev/null +++ b/supabase/.gitignore @@ -0,0 +1,3 @@ +# Supabase +.branches +.temp diff --git a/supabase/config.toml b/supabase/config.toml new file mode 100644 index 000000000..82b7702f2 --- /dev/null +++ b/supabase/config.toml @@ -0,0 +1,82 @@ +# A string used to distinguish different Supabase projects on the same host. Defaults to the working +# directory name when running `supabase init`. +project_id = "vercel-ai-chatbot" + +[api] +# Port to use for the API URL. +port = 54321 +# Schemas to expose in your API. Tables, views and stored procedures in this schema will get API +# endpoints. public and storage are always included. +schemas = ["public", "storage", "graphql_public"] +# Extra schemas to add to the search_path of every request. public is always included. +extra_search_path = ["public", "extensions"] +# The maximum number of rows returns from a view, table, or stored procedure. Limits payload size +# for accidental or malicious requests. +max_rows = 1000 + +[db] +# Port to use for the local database URL. +port = 54322 +# The database major version to use. This has to be the same as your remote database's. Run `SHOW +# server_version;` on the remote database to check. +major_version = 15 + +[studio] +# Port to use for Supabase Studio. +port = 54323 + +# Email testing server. Emails sent with the local dev setup are not actually sent - rather, they +# are monitored, and you can view the emails that would have been sent from the web interface. +[inbucket] +# Port to use for the email testing server web interface. +port = 54324 +smtp_port = 54325 +pop3_port = 54326 + +[storage] +# The maximum file size allowed (e.g. "5MB", "500KB"). +file_size_limit = "50MiB" + +[auth] +# The base URL of your website. Used as an allow-list for redirects and for constructing URLs used +# in emails. +site_url = "http://localhost:3000" +# A list of *exact* URLs that auth providers are permitted to redirect to post authentication. +additional_redirect_urls = ["http://localhost:3000/api/auth/callback"] +# How long tokens are valid for, in seconds. Defaults to 3600 (1 hour), maximum 604,800 seconds (one +# week). +jwt_expiry = 3600 +# Allow/disallow new user signups to your project. +enable_signup = true + +[auth.email] +# Allow/disallow new user signups via email to your project. +enable_signup = true +# If enabled, a user will be required to confirm any email change on both the old, and new email +# addresses. If disabled, only the new email is required to confirm. +double_confirm_changes = true +# If enabled, users need to confirm their email address before signing in. +enable_confirmations = false + +# Use an external OAuth provider. The full list of providers are: `apple`, `azure`, `bitbucket`, +# `discord`, `facebook`, `github`, `gitlab`, `google`, `keycloak`, `linkedin`, `notion`, `twitch`, +# `twitter`, `slack`, `spotify`, `workos`, `zoom`. +[auth.external.github] +enabled = true +client_id = "env(AUTH_GITHUB_ID)" +secret = "env(AUTH_GITHUB_SECRET)" +# Overrides the default auth redirectUrl. +redirect_uri = "" +# Overrides the default auth provider URL. Used to support self-hosted gitlab, single-tenant Azure, +# or any other third-party OIDC providers. +url = "" + +[analytics] +enabled = false +port = 54327 +vector_port = 54328 +# Setup BigQuery project to enable log viewer on local development stack. +# See: https://supabase.com/docs/guides/getting-started/local-development#enabling-local-logging +gcp_project_id = "" +gcp_project_number = "" +gcp_jwt_path = "supabase/gcloud.json" diff --git a/supabase/migrations/20230707053030_init.sql b/supabase/migrations/20230707053030_init.sql new file mode 100644 index 000000000..a3d3e0f34 --- /dev/null +++ b/supabase/migrations/20230707053030_init.sql @@ -0,0 +1,34 @@ +create table "public"."chats" ( + "id" text not null, + "user_id" uuid null default auth.uid (), + "payload" jsonb +); + +CREATE UNIQUE INDEX chats_pkey ON public.chats USING btree (id); + +alter table "public"."chats" add constraint "chats_pkey" PRIMARY KEY using index "chats_pkey"; + +alter table "public"."chats" add constraint "chats_user_id_fkey" FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE not valid; + +alter table "public"."chats" validate constraint "chats_user_id_fkey"; + +-- RLS +alter table "public"."chats" enable row level security; + +create policy "Allow public read for shared chats" +on "public"."chats" +as permissive +for select +to public +using (((payload ->> 'sharePath'::text) IS NOT NULL)); + +create policy "Allow full access to own chats" +on "public"."chats" +as permissive +for all +to authenticated +using ((auth.uid() = user_id)) +with check ((auth.uid() = user_id)); + + + diff --git a/supabase/seed.sql b/supabase/seed.sql new file mode 100644 index 000000000..e69de29bb diff --git a/tsconfig.json b/tsconfig.json index 9f91b9bae..1b03bc259 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -24,12 +24,6 @@ ], "strictNullChecks": true }, - "include": [ - "next-env.d.ts", - "next-auth.d.ts", - "**/*.ts", - "**/*.tsx", - ".next/types/**/*.ts" - ], + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "exclude": ["node_modules"] }