diff --git a/app/[username]/profile/delete-account.tsx b/app/[username]/profile/delete-account.tsx index 0e6a40a..3d249ac 100644 --- a/app/[username]/profile/delete-account.tsx +++ b/app/[username]/profile/delete-account.tsx @@ -12,14 +12,14 @@ import { AlertDialogTrigger, } from "@/components/ui/alert-dialog"; import { Button } from "@/components/ui/button"; +import { signOut } from '@/lib/auth-client'; import { Trash2 } from "lucide-react"; -import { signOut } from 'next-auth/react'; import { useState } from 'react'; import { toast } from "sonner"; - +import { useRouter } from "next/navigation"; export function DeleteAccount({ userId }: { userId: string }) { const [isDeleting, setIsDeleting] = useState(false); - + const router = useRouter() const handleDeleteAccount = async () => { setIsDeleting(true); try { @@ -31,7 +31,9 @@ export function DeleteAccount({ userId }: { userId: string }) { toast.error('Failed to delete account'); } - await signOut({ redirectTo: '/' }); + await signOut() + + router.push('/') } catch (error) { console.error('Error deleting account:', error); toast.error('Failed to delete account'); @@ -68,4 +70,4 @@ export function DeleteAccount({ userId }: { userId: string }) { ); -} \ No newline at end of file +} diff --git a/app/[username]/profile/page.tsx b/app/[username]/profile/page.tsx index bd15e69..4db5383 100644 --- a/app/[username]/profile/page.tsx +++ b/app/[username]/profile/page.tsx @@ -17,6 +17,7 @@ import { redirect } from "next/navigation"; import { Suspense } from "react"; import { DeleteAccount } from "./delete-account"; import { ImageModal } from "./image-modal"; +import { headers } from 'next/headers'; export const dynamic = 'force-dynamic' @@ -118,7 +119,9 @@ export async function generateMetadata( } export default async function ProfilePage({ params }: { params: { username: string } }) { - const currentUser = await auth(); + const currentUser = await auth.api.getSession({ + headers: headers() + }); const profile = await getUser(params.username, currentUser?.user?.name === params.username); const isOwnProfile = currentUser?.user && profile && currentUser.user?.id === profile.id; @@ -210,4 +213,4 @@ export default async function ProfilePage({ params }: { params: { username: stri ); -} \ No newline at end of file +} diff --git a/app/actions.ts b/app/actions.ts index 0ab9945..6b75bf4 100644 --- a/app/actions.ts +++ b/app/actions.ts @@ -3,10 +3,13 @@ import { auth } from "@/lib/auth"; import { prisma } from "@/lib/prisma"; import { revalidatePath } from "next/cache"; +import { headers } from 'next/headers'; export async function deleteUserImage(id: string): Promise { try { - const userData = await auth(); + const userData = await auth.api.getSession({ + headers: headers() + }); const name = userData?.user?.name || ""; const userId = userData?.user?.id; if (!userData?.user || !userId) return new Response("Not logged in", { status: 401 }); @@ -51,4 +54,4 @@ export async function deleteUserImage(id: string): Promise { console.error(error); return new Response("Internal server error", { status: 500 }); } -} \ No newline at end of file +} diff --git a/app/api/auth/[...all]/route.ts b/app/api/auth/[...all]/route.ts new file mode 100644 index 0000000..52c8f43 --- /dev/null +++ b/app/api/auth/[...all]/route.ts @@ -0,0 +1,4 @@ +import { toNextJsHandler } from "better-auth/next-js"; +import { auth } from "@/lib/auth"; + +export const { POST, GET } = toNextJsHandler(auth); diff --git a/app/api/auth/[...nextauth]/route.ts b/app/api/auth/[...nextauth]/route.ts deleted file mode 100644 index aecea4c..0000000 --- a/app/api/auth/[...nextauth]/route.ts +++ /dev/null @@ -1,2 +0,0 @@ -import { handlers } from "@/lib/auth" -export const { GET, POST } = handlers \ No newline at end of file diff --git a/app/api/save/get-last-saved/route.ts b/app/api/save/get-last-saved/route.ts index 7aa3259..ed88e16 100644 --- a/app/api/save/get-last-saved/route.ts +++ b/app/api/save/get-last-saved/route.ts @@ -1,9 +1,12 @@ import { auth } from "@/lib/auth"; import { prisma } from "@/lib/prisma"; +import { headers } from 'next/dist/client/components/headers'; -export async function GET(request : Request) { +export async function GET(request : Request) { try{ - const userData = await auth(); + const userData = await auth.api.getSession({ + headers: headers() + }); const userId = userData?.user?.id; if(!userData?.user || !userId){ @@ -14,7 +17,7 @@ export async function GET(request : Request) { where: { userId }, orderBy: { updatedAt: "desc" }, }) - + if (!lastSavedImage) { return new Response(null, { status: 200 }); } @@ -23,4 +26,4 @@ export async function GET(request : Request) { } catch(error: any){ return new Response(error.message, { status: 500 }); } -} \ No newline at end of file +} diff --git a/app/api/save/route.ts b/app/api/save/route.ts index 64afa1b..197fe73 100644 --- a/app/api/save/route.ts +++ b/app/api/save/route.ts @@ -3,6 +3,7 @@ import { prisma } from "@/lib/prisma"; import { useLastSavedTime } from "@/store/use-last-save"; import { diff, Jimp } from 'jimp'; import { revalidatePath } from "next/cache"; +import { headers } from 'next/headers'; export const runtime = "nodejs"; @@ -157,7 +158,9 @@ async function saveOrUpdateUserImage(userId: string, imageUrl: string, identifie export async function POST(request: Request) { try { - const userData = await auth(); + const userData = await auth.api.getSession({ + headers: headers() + }); const userId = userData?.user?.id; if (!userData?.user || !userId) { @@ -195,4 +198,4 @@ export async function POST(request: Request) { console.error(error); return new Response(error.message, { status: 500 }); } -} \ No newline at end of file +} diff --git a/app/api/users/route.ts b/app/api/users/route.ts index 12fca98..8286dc3 100644 --- a/app/api/users/route.ts +++ b/app/api/users/route.ts @@ -1,5 +1,6 @@ -import { auth, signOut } from "@/lib/auth"; +import { auth } from "@/lib/auth"; import { prisma } from "@/lib/prisma"; +import { headers } from 'next/headers'; async function deleteImageFromCloudflare(imageId: string): Promise { const response = await fetch( @@ -19,7 +20,9 @@ async function deleteImageFromCloudflare(imageId: string): Promise { export async function DELETE(request: Request) { try { - const userData = await auth(); + const userData = await auth.api.getSession({ + headers: headers() + }); const { searchParams } = new URL(request.url); const userId = searchParams.get("id"); @@ -45,7 +48,7 @@ export async function DELETE(request: Request) { await Promise.all(deleteImagePromises); - await signOut({ redirect: false }); + await auth.api.signOut({ headers: headers() }) await prisma.user.delete({ where: { id: userId } }); @@ -54,4 +57,4 @@ export async function DELETE(request: Request) { console.error("Error deleting account:", error.message); return Response.json({ error: error.message }, { status: 500 }); } -} \ No newline at end of file +} diff --git a/app/community/page.tsx b/app/community/page.tsx index 70c69b1..a08ea81 100644 --- a/app/community/page.tsx +++ b/app/community/page.tsx @@ -2,7 +2,9 @@ import { auth } from "@/lib/auth"; import { prisma } from "@/lib/prisma"; import { Metadata } from "next"; import { ImageGallery } from "./image-gallery"; - +import { headers } from 'next/headers'; +import { Button } from '@/components/ui/button'; +import Link from "next/link"; export const dynamic = 'force-dynamic' export const metadata: Metadata = { @@ -84,18 +86,34 @@ async function getAllPublicImages() { export default async function CommunityPage() { const images = await getAllPublicImages(); - const currentUser = await auth(); + const currentUser = await auth.api.getSession({ + headers: headers() + }); return (
-
- +
+ {images.length > 0 ? ( + + ) : ( +
+

+ Community Gallery +

+

+ Be the first to share your creations with the community! Head over to the Studio to get started. +

+ +
+ )}
); -} \ No newline at end of file +} diff --git a/bun.lockb b/bun.lockb index 8c3725b..2adb717 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/components/auth/sign-in.tsx b/components/auth/sign-in.tsx index b005a3e..b81d7ef 100644 --- a/components/auth/sign-in.tsx +++ b/components/auth/sign-in.tsx @@ -1,4 +1,4 @@ -import { signIn } from "@/lib/auth" +import { signIn } from "@/lib/auth-client" import { Button } from "../ui/button" export function SignIn() { @@ -6,7 +6,7 @@ export function SignIn() {
{ "use server" - await signIn("github") + await signIn.social({ provider: "github" }) }} > diff --git a/components/login-modal.tsx b/components/login-modal.tsx deleted file mode 100644 index d5033a6..0000000 --- a/components/login-modal.tsx +++ /dev/null @@ -1,86 +0,0 @@ -"use client" - -import { useState } from "react" -import { Button } from "@/components/ui/button" -import { Input } from "@/components/ui/input" -import { Label } from "@/components/ui/label" -import { - Dialog, - DialogContent, - DialogDescription, - DialogHeader, - DialogTitle, - DialogTrigger, -} from "@/components/ui/dialog" -import { Github, Mail } from 'lucide-react' - -export function LoginModal() { - const [isOpen, setIsOpen] = useState(false) - const [isSignUp, setIsSignUp] = useState(false) - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault() - // Handle form submission - console.log("Form submitted") - } - - return ( - - - - - - - {isSignUp ? "Sign Up" : "Sign In"} - - {isSignUp - ? "Create an account to get started" - : "Sign in to your account"} - - - -
- - -
-
- - -
- - -
-
- -
-
- - Or continue with - -
-
-
- - -
-
- {isSignUp ? "Already have an account?" : "Don't have an account?"}{" "} - -
-
-
- ) -} - diff --git a/components/navigation/marketing.tsx b/components/navigation/marketing.tsx index 446a0d0..a908bed 100644 --- a/components/navigation/marketing.tsx +++ b/components/navigation/marketing.tsx @@ -1,13 +1,14 @@ "use client"; -import * as React from "react"; +import { authClient, signIn, useSession } from "@/lib/auth-client"; +import { ChevronDown, GalleryVertical, LogOut, Menu, UserIcon } from 'lucide-react'; import Link from "next/link"; -import { signIn, signOut, useSession } from "next-auth/react"; -import { ChevronDown, Frame, LogOut, Menu, User } from 'lucide-react'; +import * as React from "react"; +import { type User } from 'better-auth'; +import { useRouter } from 'next/navigation'; -import { cn } from "@/lib/utils"; -import Spinner from '@/components/spinner/spinner'; import { ThemeToggle } from '@/components/theme-toggle'; +import { cn } from "@/lib/utils"; import logo from "@/public/logo.svg"; import icon from "@/public/logo.webp"; @@ -15,9 +16,8 @@ import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Button } from "@/components/ui/button"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"; -import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from "@/components/ui/sheet"; import { NavigationMenu, NavigationMenuContent, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger } from "@/components/ui/navigation-menu"; -import { type User as NextAuthUser } from "next-auth"; +import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from "@/components/ui/sheet"; const productItems = [ { @@ -74,7 +74,7 @@ const ListItem = React.forwardRef< ListItem.displayName = "ListItem"; export const UserAvatar = ({ user, className = "h-8 w-8" }: { - user: NextAuthUser | undefined, + user: User | undefined, className?: string }) => ( @@ -109,62 +109,30 @@ const SignOutDialog = ({ onSignOut }: { onSignOut: () => Promise } ); export function Navbar() { - const { data: session, status } = useSession(); + const { data: session, isPending: loading, error } = useSession(); + const router = useRouter(); const [isOpen, setIsOpen] = React.useState(false); - const handleSignOut = async () => { - await signOut({ redirect: true, callbackUrl: '/' }); - }; - - const platformItems = [ + const platformItems = React.useMemo(() => session ? [ { title: "About", href: "/about", description: "Learn more about our mission and team." }, { title: "Community", href: "/community", description: "Join our growing community of developers." }, { title: "Studio", href: "/studio", description: "Access your development workspace." }, - ...(status === 'authenticated' ? [ - { - title: "Profile", - href: `/${session?.user?.name}/profile`, - description: "Manage your account settings and preferences." - } - ] : []) - ]; - - const renderAuthButton = () => { - if (status === 'loading') { - return ( - - ); - } - - if (status === 'authenticated' && session) { - return ( - - - - - - - - Account - - - - - - - ); + { + title: "Profile", + href: `/${session?.user?.name}/profile`, + description: "Manage your account settings and preferences." } + ] : [], [session]); - return ( - - ); - }; + const handleSignOut = React.useCallback(async () => { + await authClient.signOut({ + fetchOptions: { + onSuccess: () => { + router.push('/') + } + } + }) + }, [router]); return ( ); -} \ No newline at end of file +} diff --git a/components/navigation/studio.tsx b/components/navigation/studio.tsx index ebc480f..c17bd5b 100644 --- a/components/navigation/studio.tsx +++ b/components/navigation/studio.tsx @@ -1,7 +1,7 @@ "use client"; import { ChevronDown, Frame, GalleryVertical, Github, Info, LogOut, LucideIcon, Menu, User } from "lucide-react"; -import { signIn, signOut, useSession } from "next-auth/react"; +import { signIn, signOut, useSession } from "@/lib/auth-client"; import Link from "next/link"; import { usePathname } from "next/navigation"; import * as React from "react"; @@ -62,12 +62,12 @@ const ListItem = React.forwardRef, ListItemProps>( ListItem.displayName = "ListItem"; export function StudioNavbar() { - const { data: session, status } = useSession(); + const { data: session, isPending: loading, error } = useSession(); const pathname = usePathname(); const [isOpen, setIsOpen] = React.useState(false); const handleSignOut = async () => { - await signOut({ redirect: true, callbackUrl: '/' }); + await signOut() }; const resourceItems: ResourceItem[] = [ @@ -97,82 +97,12 @@ export function StudioNavbar() { } ]; - const renderAuthButton = () => { - if (status === 'loading') { - return ( - - ); - } - - if (status === 'authenticated' && session) { - return ( - - - - - - - - Account - - - - - Community - - - - - - e.preventDefault()} className="text-red-600 dark:text-red-400"> - Sign Out - - - - - Are you sure you want to sign out? - - You won't be able to save your changes. - - - - Cancel - - Sign Out - - - - - - - ); - } - - return ( - - ); - }; - return (
Seleneo Logo Seleneo - {/* - Pre-release - */} @@ -206,7 +136,62 @@ export function StudioNavbar() { "h-4 w-px bg-border": pathname.includes("/studio") })} /> - {renderAuthButton()} + {loading ? ( + + ) : session ? ( + + + + + + + + Account + + + + + Community + + + + + + e.preventDefault()} className="text-red-600 dark:text-red-400"> + Sign Out + + + + + Are you sure you want to sign out? + + You won't be able to save your changes. + + + + Cancel + + Sign Out + + + + + + + ) : ( + + )}
@@ -224,7 +209,7 @@ export function StudioNavbar() {
- {status === 'authenticated' && session && ( + {session && (
@@ -252,7 +237,7 @@ export function StudioNavbar() { ))}
- {status === 'authenticated' ? ( + {session ? (
); -} \ No newline at end of file +} diff --git a/components/studio/export/handlers.tsx b/components/studio/export/handlers.tsx index a003876..ee76dbe 100644 --- a/components/studio/export/handlers.tsx +++ b/components/studio/export/handlers.tsx @@ -16,6 +16,7 @@ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/comp import { useImageOptions } from '@/store/use-image-options' import { useLastSavedTime } from '@/store/use-last-save' import { useResizeCanvas } from '@/store/use-resize-canvas' +import { Auth, type Session } from 'better-auth' import { saveAs } from 'file-saver' import { Copy, Download, Eye, EyeOff, Save } from 'lucide-react' import { useState } from 'react' @@ -24,7 +25,7 @@ import { toast } from 'sonner' interface ExportActionsProps { quality: number fileType: FileType - sessionStatus: "authenticated" | "loading" | "unauthenticated" + sessionStatus: any } type SuccessResponse = DuplicateResponse | NewSaveResponse; @@ -201,8 +202,8 @@ export function ExportActions({ quality, fileType, sessionStatus }: ExportAction variant="ghost" size="icon" onClick={handleCloudSave} - disabled={isCopying || isDownloading || isSaving || sessionStatus !== 'authenticated'} - title={sessionStatus === 'unauthenticated' ? 'Login to save your design!' : 'Save your design to the cloud'} + disabled={isCopying || isDownloading || isSaving || !sessionStatus} + title={sessionStatus ? 'Login to save your design!' : 'Save your design to the cloud'} className="hover:bg-background" > {isSaving ? : } @@ -269,4 +270,4 @@ export function ExportActions({ quality, fileType, sessionStatus }: ExportAction
) -} \ No newline at end of file +} diff --git a/components/studio/export/index.tsx b/components/studio/export/index.tsx index 76aa649..5e06c9b 100644 --- a/components/studio/export/index.tsx +++ b/components/studio/export/index.tsx @@ -4,12 +4,12 @@ import { ExportActions } from '@/components/studio/export/handlers' import { ExportQualityPopover, FileTypePopover } from '@/components/studio/export/popovers' import { FileType } from '@/components/studio/export/types' import { cn } from '@/lib/utils' -import { useSession } from 'next-auth/react' +import { useSession } from '@/lib/auth-client' import { usePathname } from 'next/navigation' import { useState } from 'react' export function ExportOptions() { - const session = useSession() + const { data: session } = useSession() const [quality, setQuality] = useState(1) const [fileType, setFileType] = useState('PNG') const pathname = usePathname() @@ -17,7 +17,7 @@ export function ExportOptions() { return ( <>
- +
@@ -26,4 +26,4 @@ export function ExportOptions() {
) -} \ No newline at end of file +} diff --git a/docker-compose.yml b/docker-compose.yml index 09f0862..8147356 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,12 +1,17 @@ -version: '3' +version: "3.8" + services: postgres: - image: postgres:latest + image: postgres:15 + container_name: postgres_db ports: - "5432:5432" environment: - POSTGRES_USER: root - POSTGRES_PASSWORD: postgres123 - POSTGRES_DB: seleneo_db + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_DB: ${POSTGRES_DB} volumes: - - ./data:/var/lib/postgresql/data + - postgres_data:/var/lib/postgresql/data + +volumes: + postgres_data: diff --git a/lib/auth-client.ts b/lib/auth-client.ts new file mode 100644 index 0000000..e55e9e6 --- /dev/null +++ b/lib/auth-client.ts @@ -0,0 +1,7 @@ +import { createAuthClient } from "better-auth/react"; + +export const authClient = createAuthClient({ + baseURL: process.env.BASE_URL! // NOTE: This is optional if our API base URL matches the frontend +}); + +export const { signIn, signOut, useSession } = authClient; diff --git a/lib/auth.ts b/lib/auth.ts index 58b2158..6c6b1ee 100644 --- a/lib/auth.ts +++ b/lib/auth.ts @@ -1,40 +1,32 @@ -import { prisma } from "@/lib/prisma" -import { PrismaAdapter } from "@auth/prisma-adapter" -import NextAuth from "next-auth" -import GitHub from "next-auth/providers/github" +import { betterAuth } from "better-auth"; +import { prismaAdapter } from "better-auth/adapters/prisma"; +import { prisma } from './prisma'; -export const { handlers, signIn, signOut, auth } = NextAuth({ - providers: [GitHub({ - profile(profile) { - return { - id: profile.id.toString(), - name: profile.login, - email: profile.email, - image: profile.avatar_url - } - } - })], - adapter: PrismaAdapter(prisma), - callbacks: { - async session({ session, user }) { - session.user.id = user.id - return session - }, - async signIn({ user, profile }) { - if (profile && 'login' in profile) { - const dbUser = await prisma.user.findUnique({ - where: { id: user.id }, - select: { name: true } - }) - - if (dbUser?.name?.includes(' ')) { - await prisma.user.update({ - where: { id: user.id }, - data: { name: profile.login as string } - }) +export const auth = betterAuth({ + socialProviders: { + github: { + clientId: process.env.AUTH_GITHUB_ID!, + clientSecret: process.env.AUTH_GITHUB_SECRET!, + mapProfileToUser: (profile) => { + return { + id: profile.id.toString(), + name: profile.login, + email: profile.email, + image: profile.avatar_url, } - } - return true - }, + }, + } + }, + accounts: { + fields: { + accountId: "providerAccountId", + refreshToken: "refresh_token", + accessToken: "access_token", + accessTokenExpiresAt: "access_token_expires", + idToken: "id_token", + } }, -}) \ No newline at end of file + database: prismaAdapter(prisma, { + provider: "postgresql", + }), +}); diff --git a/package.json b/package.json index 74bb81a..3cbefc4 100644 --- a/package.json +++ b/package.json @@ -13,10 +13,9 @@ "db:studio": "prisma studio" }, "dependencies": { - "@auth/prisma-adapter": "^2.7.4", "@fseehawer/react-circular-slider": "^2.7.0", "@hookform/resolvers": "^3.9.1", - "@prisma/client": "latest", + "@prisma/client": "6.2.1", "@radix-ui/react-accordion": "^1.2.2", "@radix-ui/react-alert-dialog": "^1.1.4", "@radix-ui/react-aspect-ratio": "^1.1.1", @@ -67,6 +66,7 @@ "@types/react": "18.2.21", "@types/react-dom": "18.2.7", "autoprefixer": "10.4.15", + "better-auth": "^1.1.18", "canvas": "^3.0.1", "class-variance-authority": "^0.7.1", "clsx": "^2.0.0", @@ -86,7 +86,6 @@ "lucide-react": "^0.469.0", "match-sorter": "^6.3.4", "next": "14.2.16", - "next-auth": "beta", "next-themes": "^0.4.4", "pngjs": "^7.0.0", "postcss": "8.4.28", @@ -130,6 +129,6 @@ "lint-staged": "^15.2.7", "prettier": "3.0.3", "prettier-plugin-tailwindcss": "^0.5.14", - "prisma": "latest" + "prisma": "^6.2.1" } } diff --git a/prisma/migrations/20250220021307_better_auth_migration/migration.sql b/prisma/migrations/20250220021307_better_auth_migration/migration.sql new file mode 100644 index 0000000..b391567 --- /dev/null +++ b/prisma/migrations/20250220021307_better_auth_migration/migration.sql @@ -0,0 +1,102 @@ +/* + Warnings: + + - You are about to drop the `Account` table. If the table is not empty, all the data it contains will be lost. + - You are about to drop the `Session` table. If the table is not empty, all the data it contains will be lost. + - You are about to drop the `User` table. If the table is not empty, all the data it contains will be lost. + +*/ +-- DropForeignKey +ALTER TABLE "Account" DROP CONSTRAINT "Account_userId_fkey"; + +-- DropForeignKey +ALTER TABLE "Session" DROP CONSTRAINT "Session_userId_fkey"; + +-- DropForeignKey +ALTER TABLE "UserImage" DROP CONSTRAINT "UserImage_userId_fkey"; + +-- DropTable +DROP TABLE "Account"; + +-- DropTable +DROP TABLE "Session"; + +-- DropTable +DROP TABLE "User"; + +-- CreateTable +CREATE TABLE "account" ( + "id" TEXT NOT NULL, + "userId" TEXT NOT NULL, + "provider" TEXT, + "providerAccountId" TEXT NOT NULL, + "refresh_token" TEXT, + "access_token" TEXT, + "access_token_expires" TIMESTAMP(3), + "scope" TEXT, + "id_token" TEXT, + "providerId" TEXT NOT NULL, + "refreshTokenExpiresAt" TIMESTAMP(3), + "password" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "account_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "session" ( + "id" TEXT NOT NULL, + "sessionToken" TEXT NOT NULL, + "userId" TEXT NOT NULL, + "expires" TIMESTAMP(3) NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL, + "updatedAt" TIMESTAMP(3) NOT NULL, + "ipAddress" TEXT, + "userAgent" TEXT, + + CONSTRAINT "session_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "user" ( + "id" TEXT NOT NULL, + "name" TEXT, + "email" TEXT, + "emailVerified" BOOLEAN NOT NULL DEFAULT false, + "image" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "user_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "verification" ( + "id" TEXT NOT NULL, + "identifier" TEXT NOT NULL, + "value" TEXT NOT NULL, + "expiresAt" TIMESTAMP(3) NOT NULL, + "createdAt" TIMESTAMP(3), + "updatedAt" TIMESTAMP(3), + + CONSTRAINT "verification_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "account_provider_providerAccountId_key" ON "account"("provider", "providerAccountId"); + +-- CreateIndex +CREATE UNIQUE INDEX "session_sessionToken_key" ON "session"("sessionToken"); + +-- CreateIndex +CREATE UNIQUE INDEX "user_email_key" ON "user"("email"); + +-- AddForeignKey +ALTER TABLE "account" ADD CONSTRAINT "account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "session" ADD CONSTRAINT "session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "UserImage" ADD CONSTRAINT "UserImage_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index e00dfd5..bc9581f 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -1,3 +1,4 @@ + generator client { provider = "prisma-client-js" } @@ -8,42 +9,56 @@ datasource db { } model Account { - id String @id @default(cuid()) - userId String - type String - provider String - providerAccountId String - refresh_token String? @db.Text - access_token String? @db.Text - expires_at Int? - token_type String? - scope String? - id_token String? @db.Text - session_state String? + id String @id @default(cuid()) + userId String + provider String? + accountId String @map("providerAccountId") + refreshToken String? @map("refresh_token") @db.Text + accessToken String? @map("access_token") @db.Text + accessTokenExpiresAt DateTime? @map("access_token_expires") + scope String? + idToken String? @map("id_token") @db.Text user User @relation(fields: [userId], references: [id], onDelete: Cascade) - @@unique([provider, providerAccountId]) + providerId String + refreshTokenExpiresAt DateTime? + password String? + createdAt DateTime + updatedAt DateTime + + @@unique([provider, accountId]) + @@map("account") } model Session { - id String @id @default(cuid()) - sessionToken String @unique - userId String - expires DateTime - user User @relation(fields: [userId], references: [id], onDelete: Cascade) + id String @id @default(cuid()) + token String @unique @map("sessionToken") + userId String + expiresAt DateTime @map("expires") + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + createdAt DateTime + updatedAt DateTime + ipAddress String? + userAgent String? + + @@map("session") } model User { id String @id @default(cuid()) name String? email String? @unique - emailVerified DateTime? + emailVerified Boolean @default(false) image String? accounts Account[] sessions Session[] UserImage UserImage[] + createdAt DateTime + updatedAt DateTime + + @@map("user") } model VerificationToken { @@ -60,12 +75,23 @@ enum Visibility { } model UserImage { - id String @id @default(cuid()) - identifier String @unique @default("") - userId String - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) - cloudflareUrl String - visibility Visibility - user User @relation(fields: [userId], references: [id], onDelete: Cascade) + id String @id @default(cuid()) + identifier String @unique @default("") + userId String + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) + cloudflareUrl String + visibility Visibility + user User @relation(fields: [userId], references: [id], onDelete: Cascade) +} + +model Verification { + id String @id + identifier String + value String + expiresAt DateTime + createdAt DateTime? + updatedAt DateTime? + + @@map("verification") } diff --git a/providers/index.tsx b/providers/index.tsx index 170e315..b4b834a 100644 --- a/providers/index.tsx +++ b/providers/index.tsx @@ -1,7 +1,6 @@ 'use client' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' -import { SessionProvider } from 'next-auth/react' import { ThemeProvider } from 'next-themes' import { Provider } from 'react-wrap-balancer' import { Toaster } from 'sonner' @@ -22,13 +21,10 @@ export function Providers({ children }: ProviderProps) { }) return ( - - - + {children} -