From d032c0c720501829b73a42c34b01c982619f6d9e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Yusuf=20Akg=C3=BCl?= <s86116@bht-berlin.de>
Date: Sun, 4 Jun 2023 05:19:58 +0200
Subject: [PATCH] beg auth change

---
 app/(auth)/login/page.tsx           |  51 ++++++--
 app/(auth)/signup/page.tsx          |  62 +++++++--
 app/(content)/(home)/home/page.tsx  |   4 +-
 app/(content)/followers/page.tsx    |  11 +-
 app/api/auth/[...nextauth]/route.ts |   1 +
 app/api/likes/likeService.ts        |  12 +-
 app/api/messages/route.ts           |  10 +-
 app/api/signup/route.ts             |  22 ++--
 app/api/user/[userid].ts            |  28 ----
 app/api/user/index.ts               |  22 ----
 components/auth-login-form.tsx      |  69 ----------
 components/auth-signup-form.tsx     |  71 ----------
 components/icons.tsx                |   8 +-
 components/logo.tsx                 |  10 ++
 components/nav.tsx                  |  22 ++--
 components/user-auth-form.tsx       | 193 ++++++++++++++++++++++++++++
 components/user-item.tsx            |   6 +-
 env.mjs                             |  33 +++++
 lib/auth.ts                         |  89 +++++++++----
 lib/db.ts                           |  23 ++--
 lib/igdb.ts                         |   9 +-
 lib/utils.ts                        |   4 +-
 lib/validations/auth.ts             |   2 +
 next.config.js => next.config.mjs   |   6 +-
 package-lock.json                   | 112 +++++++++++++++-
 package.json                        |   7 +-
 prisma/schema.prisma                | 107 +++++++++++----
 tsconfig.json                       |   3 +-
 28 files changed, 667 insertions(+), 330 deletions(-)
 delete mode 100644 app/api/user/[userid].ts
 delete mode 100644 app/api/user/index.ts
 delete mode 100644 components/auth-login-form.tsx
 delete mode 100644 components/auth-signup-form.tsx
 create mode 100644 components/logo.tsx
 create mode 100644 components/user-auth-form.tsx
 create mode 100644 env.mjs
 rename next.config.js => next.config.mjs (61%)

diff --git a/app/(auth)/login/page.tsx b/app/(auth)/login/page.tsx
index f499915..5cadd64 100644
--- a/app/(auth)/login/page.tsx
+++ b/app/(auth)/login/page.tsx
@@ -1,17 +1,48 @@
-import { LoginForm } from '@/components/auth-login-form'
+import { Icons } from '@/components/icons'
+import { GameUnityLogo } from '@/components/logo'
+import { buttonVariants } from '@/components/ui/button'
+import { UserAuthForm } from '@/components/user-auth-form'
+import { cn } from '@/lib/utils'
 import Link from 'next/link'
 
+export const metadata = {
+    title: "Login",
+    description: "Login to your account",
+}
+
 export default function LoginPage() {
     return (
-        <div className="h-screen w-screen flex justify-center items-center bg-slate-100">
-            <div className="sm:shadow-xl px-8 pb-8 pt-12 sm:bg-black rounded-xl space-y-12">
-                <h1 className="font-semibold text-2xl">Login</h1>
-                <LoginForm />
-                <p className="text-center">
-                    Need to create an account?{' '}
-                    <Link className="text-indigo-500 hover:underline" href="/signup">
-                        Create Account
-                    </Link>{' '}
+        <div className="container flex min-h-screen w-screen flex-col items-center justify-center">
+            <Link
+                href="/"
+                className={cn(
+                    buttonVariants({ variant: "ghost" }),
+                    "absolute left-4 top-4 md:left-8 md:top-8"
+                )}
+            >
+                <>
+                    <Icons.chevronLeft />
+                    Back
+                </>
+            </Link>
+            <div className="mx-auto flex w-full flex-col justify-center space-y-6 sm:w-[350px]">
+                <div className="flex flex-col items-center space-y-2 text-center">
+                    <GameUnityLogo className="h-10 w-10" />
+                    <h1 className="text-2xl font-semibold tracking-tight">
+                        Welcome back
+                    </h1>
+                    <p className="text-sm text-muted-foreground">
+                        Enter your email to sign in to your account
+                    </p>
+                </div>
+                <UserAuthForm type='login' />
+                <p className="px-8 text-center text-sm text-muted-foreground">
+                    <Link
+                        href="/signup"
+                        className="hover:text-brand underline underline-offset-4"
+                    >
+                        Don&apos;t have an account? Sign Up
+                    </Link>
                 </p>
             </div>
         </div>
diff --git a/app/(auth)/signup/page.tsx b/app/(auth)/signup/page.tsx
index ea1b9f6..da3422f 100644
--- a/app/(auth)/signup/page.tsx
+++ b/app/(auth)/signup/page.tsx
@@ -1,18 +1,58 @@
-import { SignupForm } from '@/components/auth-signup-form'
+import { GameUnityLogo } from '@/components/logo'
+import { buttonVariants } from '@/components/ui/button'
+import { UserAuthForm } from '@/components/user-auth-form'
+import { cn } from '@/lib/utils'
 import Link from 'next/link'
 
+export const metadata = {
+    title: "Create an account",
+    description: "Create an account to get started.",
+}
+
 export default function SignupPage() {
     return (
-        <div className="h-screen w-screen flex justify-center items-center bg-slate-100">
-            <div className="sm:shadow-xl px-8 pb-8 pt-12 sm:bg-black rounded-xl space-y-12">
-                <h1 className="font-semibold text-2xl">Create your Account</h1>
-                <SignupForm />
-                <p className="text-center">
-                    Have an account?{' '}
-                    <Link className="text-indigo-500 hover:underline" href="/login">
-                        Sign in
-                    </Link>{' '}
-                </p>
+        <div className="container grid h-screen w-screen flex-col items-center justify-center lg:max-w-none lg:grid-cols-2 lg:px-0">
+            <Link
+                href="/login"
+                className={cn(
+                    buttonVariants({ variant: "ghost" }),
+                    "absolute right-4 top-4 md:right-8 md:top-8"
+                )}
+            >
+                Login
+            </Link>
+            <div className="hidden h-full bg-muted lg:block" />
+            <div className="lg:p-8">
+                <div className="mx-auto flex w-full flex-col justify-center space-y-6 sm:w-[350px]">
+                    <div className="flex flex-col items-center space-y-2 text-center">
+                        <GameUnityLogo className="h-10 w-10" />
+
+                        <h1 className="text-2xl font-semibold tracking-tight">
+                            Create an account
+                        </h1>
+                        <p className="text-sm text-muted-foreground">
+                            Give yourself a username, enter your email and password below to create an account
+                        </p>
+                    </div>
+                    <UserAuthForm type='signup' />
+                    <p className="px-8 text-center text-sm text-muted-foreground">
+                        By clicking continue, you agree to our{" "}
+                        <Link
+                            href="/terms"
+                            className="hover:text-brand underline underline-offset-4"
+                        >
+                            Terms of Service
+                        </Link>{" "}
+                        and{" "}
+                        <Link
+                            href="/privacy"
+                            className="hover:text-brand underline underline-offset-4"
+                        >
+                            Privacy Policy
+                        </Link>
+                        .
+                    </p>
+                </div>
             </div>
         </div>
     )
diff --git a/app/(content)/(home)/home/page.tsx b/app/(content)/(home)/home/page.tsx
index 89b396f..d50aa7c 100644
--- a/app/(content)/(home)/home/page.tsx
+++ b/app/(content)/(home)/home/page.tsx
@@ -1,6 +1,6 @@
 import LikeButton from "@/components/like-button";
 import PostMessageForm from "@/components/post-messages";
-import { prisma } from "@/lib/db";
+import { db } from "@/lib/db";
 import { Prisma } from "@prisma/client";
 /* export const revalidate = 5; */ // revalidate this page every 60 seconds
 
@@ -12,7 +12,7 @@ type messageItemProps = {
 export default async function HomePage() {
   let messages = null
   try {
-    messages = await prisma.post.findMany({
+    messages = await db.post.findMany({
       orderBy: {
         createdAt: "desc"
       }
diff --git a/app/(content)/followers/page.tsx b/app/(content)/followers/page.tsx
index 7fbb300..ec8175d 100644
--- a/app/(content)/followers/page.tsx
+++ b/app/(content)/followers/page.tsx
@@ -1,18 +1,17 @@
-import { authOptions } from "@/app/api/auth/[...nextauth]/route";
 import FollowersList from "@/components/following-users";
 import { getServerSession } from "next-auth";
 
 export default async function Followers() {
-    const session = await getServerSession(authOptions);
+    // const session = await getServerSession(authOptions);
 
-    if (!session) {
-        return <div>Loading...</div>;
-    }
+    // if (!session) {
+    //     return <div>Loading...</div>;
+    // }
 
     return (
         <div>
             <h1>Followers Page WIP</h1>
-            <FollowersList userId={parseFloat(session.user?.id)} />
+            {/* <FollowersList userId={parseFloat(session.user?.id)} /> */}
         </div>
     )
 }
\ No newline at end of file
diff --git a/app/api/auth/[...nextauth]/route.ts b/app/api/auth/[...nextauth]/route.ts
index dbc44a8..17d6a45 100644
--- a/app/api/auth/[...nextauth]/route.ts
+++ b/app/api/auth/[...nextauth]/route.ts
@@ -4,3 +4,4 @@ import NextAuth from 'next-auth'
 const handler = NextAuth(authOptions)
 
 export { handler as GET, handler as POST }
+
diff --git a/app/api/likes/likeService.ts b/app/api/likes/likeService.ts
index 0dd748e..4353945 100644
--- a/app/api/likes/likeService.ts
+++ b/app/api/likes/likeService.ts
@@ -1,4 +1,4 @@
-import { prisma } from "@/lib/db"
+import { db } from "@/lib/db"
 import { Prisma } from "@prisma/client"
 
 type likeType = Prisma.LikeUncheckedCreateInput
@@ -12,7 +12,7 @@ export async function putLike(like: likeType): Promise<likeType | undefined> {
     // if exists delete
     // if not create
     try {
-        const actualLike = await prisma.like.findFirst({
+        const actualLike = await db.like.findFirst({
             where: {
                 id: like.id,
                 postId: like.postId,
@@ -25,13 +25,13 @@ export async function putLike(like: likeType): Promise<likeType | undefined> {
             throw Error("Message was not liked by this user")
         }
 
-        await prisma.like.delete({
+        await db.like.delete({
             where: {
                 id: actualLike.id
             }
         })
 
-        const msg = await prisma.post.update({
+        const msg = await db.post.update({
             where: {
                 id: like.postId
             },
@@ -43,14 +43,14 @@ export async function putLike(like: likeType): Promise<likeType | undefined> {
         return undefined;
 
     } catch {
-        const createdLike = await prisma.like.create({
+        const createdLike = await db.like.create({
             data: {
                 postId: like.postId,
                 userId: like.userId
             }
         })
 
-        const updatedMessage = await prisma.post.update({
+        const updatedMessage = await db.post.update({
             where: {
                 id: like.postId
             },
diff --git a/app/api/messages/route.ts b/app/api/messages/route.ts
index 56ccc74..6abf1de 100644
--- a/app/api/messages/route.ts
+++ b/app/api/messages/route.ts
@@ -1,5 +1,5 @@
 import { authOptions } from "@/lib/auth";
-import { prisma } from "@/lib/db";
+import { db } from "@/lib/db";
 import { Prisma } from "@prisma/client";
 import { getServerSession } from "next-auth/next";
 import { revalidatePath } from "next/cache";
@@ -20,11 +20,11 @@ export async function POST(req: NextRequest) {
 	console.log("router data: " + data.content, "status:")
 
 	try {
-		await prisma.post.create({
+		await db.post.create({
 			/* data: data */
 			data: {
 				content: data.content,
-				userId: parseInt(userId),
+				userId: userId,
 				published: true
 			}
 		})
@@ -34,7 +34,7 @@ export async function POST(req: NextRequest) {
 
 		return NextResponse.json({ status: 201, message: 'Message Created' })
 
-	} catch (error) {
+	} catch (error: any) {
 		console.log("fail" + error);
 	}
 	console.log("post")
@@ -49,7 +49,7 @@ export async function GET(req: NextRequest, res: NextResponse) {
 	}
 
 	try {
-		const messages = await prisma.post.findMany({
+		const messages = await db.post.findMany({
 			orderBy: {
 				createdAt: "desc"
 			}
diff --git a/app/api/signup/route.ts b/app/api/signup/route.ts
index 79937ac..da96437 100644
--- a/app/api/signup/route.ts
+++ b/app/api/signup/route.ts
@@ -1,32 +1,28 @@
-import { prisma } from '@/lib/db'
+import { db } from '@/lib/db'
 import { hash } from 'bcrypt'
 import { NextResponse } from 'next/server'
 
 export async function POST(req: Request) {
     try {
-        const { email, password } = await req.json()
+        const { username, email, password } = await req.json()
         const hashed = await hash(password, 12)
 
-        const user = await prisma.user.create({
+        const user = await db.user.create({
             data: {
+                username,
                 email,
                 password: hashed
             }
         })
 
         return NextResponse.json({
-            user: {
-                email: user.email
-            }
+            username: user.username,
+            email: user.email
         })
     } catch (err: any) {
-        return new NextResponse(
-            JSON.stringify({
-                error: err.message
-            }),
-            {
-                status: 500
-            }
+        return new NextResponse(JSON.stringify({
+            error: err.message
+        }), { status: 500 }
         )
     }
 }
\ No newline at end of file
diff --git a/app/api/user/[userid].ts b/app/api/user/[userid].ts
deleted file mode 100644
index 6a4bbd0..0000000
--- a/app/api/user/[userid].ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import { NextApiRequest, NextApiResponse } from "next";
-
-import { prisma } from "@/lib/db";
-
-export default async function handler(req: NextApiRequest, res: NextApiResponse) {
-  if (req.method !== 'GET') {
-    return res.status(405).end();
-  }
-
-  try {
-    const { userId } = req.query;
-
-    if (!userId || typeof userId !== 'string') {
-      throw new Error('Invalid ID');
-    }
-
-    const existingUser = await prisma.user.findUnique({
-      where: {
-        id: +userId
-      }
-    });
-
-    return res.status(200).json({ ...existingUser });
-  } catch (error) {
-    console.log(error);
-    return res.status(400).end();
-  }
-};
\ No newline at end of file
diff --git a/app/api/user/index.ts b/app/api/user/index.ts
deleted file mode 100644
index 851c256..0000000
--- a/app/api/user/index.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import { NextApiRequest, NextApiResponse } from "next";
-
-import { prisma } from "@/lib/db";
-
-export default async function handler(req: NextApiRequest, res: NextApiResponse) {
-  if (req.method !== 'GET') {
-    return res.status(405).end();
-  }
-
-  try {
-    const users = await prisma.user.findMany({
-      orderBy: {
-        createdAt: 'desc'
-      }
-    });
-
-    return res.status(200).json(users);
-  } catch (error) {
-    console.log(error);
-    return res.status(400).end();
-  }
-}
\ No newline at end of file
diff --git a/components/auth-login-form.tsx b/components/auth-login-form.tsx
deleted file mode 100644
index 0b430e2..0000000
--- a/components/auth-login-form.tsx
+++ /dev/null
@@ -1,69 +0,0 @@
-'use client'
-
-import { Alert } from '@/components/ui/alert'
-import { Button } from '@/components/ui/button'
-import { Input } from '@/components/ui/input'
-import { Label } from '@/components/ui/label'
-import { signIn } from 'next-auth/react'
-import { useRouter, useSearchParams } from 'next/navigation'
-import { useState } from 'react'
-
-export const LoginForm = () => {
-    const router = useRouter()
-    const searchParams = useSearchParams()
-    const callbackUrl = searchParams.get('callbackUrl') || '/home'
-    const [email, setEmail] = useState('')
-    const [password, setPassword] = useState('')
-    const [error, setError] = useState('')
-
-    const onSubmit = async (e: React.FormEvent) => {
-        e.preventDefault()
-        try {
-            const res = await signIn('credentials', {
-                redirect: false,
-                email,
-                password,
-                callbackUrl
-            })
-            console.log('Res', res)
-            if (!res?.error) {
-                router.push(callbackUrl)
-            } else {
-                setError('Invalid email or password')
-            }
-        } catch (err: any) { }
-    }
-
-    return (
-        <form onSubmit={onSubmit} className="space-y-12 w-full sm:w-[400px]">
-            <div className="grid w-full items-center gap-1.5">
-                <Label htmlFor="email">Email</Label>
-                <Input
-                    className="w-full"
-                    required
-                    value={email}
-                    onChange={(e) => setEmail(e.target.value)}
-                    id="email"
-                    type="email"
-                />
-            </div>
-            <div className="grid w-full items-center gap-1.5">
-                <Label htmlFor="password">Password</Label>
-                <Input
-                    className="w-full"
-                    required
-                    value={password}
-                    onChange={(e) => setPassword(e.target.value)}
-                    id="password"
-                    type="password"
-                />
-            </div>
-            {error && <Alert>{error}</Alert>}
-            <div className="w-full">
-                <Button className="w-full" size="lg">
-                    Login
-                </Button>
-            </div>
-        </form>
-    )
-}
\ No newline at end of file
diff --git a/components/auth-signup-form.tsx b/components/auth-signup-form.tsx
deleted file mode 100644
index 4eb7e63..0000000
--- a/components/auth-signup-form.tsx
+++ /dev/null
@@ -1,71 +0,0 @@
-'use client'
-
-import { Alert } from '@/components/ui/alert'
-import { Button } from '@/components/ui/button'
-import { Input } from '@/components/ui/input'
-import { Label } from '@/components/ui/label'
-import { signIn } from 'next-auth/react'
-import { useState } from 'react'
-
-export const SignupForm = () => {
-    const [email, setEmail] = useState('')
-    const [password, setPassword] = useState('')
-    const [error, setError] = useState<string | null>(null)
-
-    const onSubmit = async (e: React.FormEvent) => {
-        e.preventDefault()
-
-        try {
-            const res = await fetch('/api/signup', {
-                method: 'POST',
-                body: JSON.stringify({
-                    email,
-                    password
-                }),
-                headers: {
-                    'Content-Type': 'application/json'
-                }
-            })
-            if (res.ok) {
-                signIn()
-            } else {
-                setError((await res.json()).error)
-            }
-        } catch (error: any) {
-            setError(error?.message)
-        }
-    }
-
-    return (
-        <form onSubmit={onSubmit} className="space-y-12 w-full sm:w-[400px]">
-            <div className="grid w-full items-center gap-1.5">
-                <Label htmlFor="email">Email</Label>
-                <Input
-                    className="w-full"
-                    required
-                    value={email}
-                    onChange={(e) => setEmail(e.target.value)}
-                    id="email"
-                    type="email"
-                />
-            </div>
-            <div className="grid w-full items-center gap-1.5">
-                <Label htmlFor="password">Password</Label>
-                <Input
-                    className="w-full"
-                    required
-                    value={password}
-                    onChange={(e) => setPassword(e.target.value)}
-                    id="password"
-                    type="password"
-                />
-            </div>
-            {error && <Alert>{error}</Alert>}
-            <div className="w-full">
-                <Button className="w-full" size="lg">
-                    Sign up
-                </Button>
-            </div>
-        </form>
-    )
-}
\ No newline at end of file
diff --git a/components/icons.tsx b/components/icons.tsx
index e9a8c6b..6704d78 100644
--- a/components/icons.tsx
+++ b/components/icons.tsx
@@ -11,6 +11,7 @@ import {
     File,
     FileText,
     Gamepad2,
+    Github,
     Heart,
     HelpCircle,
     Home,
@@ -71,12 +72,13 @@ export const Icons: IconsType = {
     help: HelpCircle, // Help Nav
     sun: SunMedium, // Light Mode Toggle Nav
     moon: Moon, // Dark Mode Toggle Nav
+    arrowupline: ArrowUpToLine, // Back to Top Button with line
     arrowdown: ArrowDown, // Descending Sort
     heart: Heart, // Like Button
-    arrowupline: ArrowUpToLine, // Back to Top Button
+    chevronLeft: ChevronLeft, // Back Login Arrow
+    spinner: Loader2, // Loading Spinner
+    github: Github, // Github Icon
     close: X,
-    spinner: Loader2,
-    chevronLeft: ChevronLeft,
     chevronRight: ChevronRight,
     trash: Trash,
     post: FileText,
diff --git a/components/logo.tsx b/components/logo.tsx
new file mode 100644
index 0000000..145e3c7
--- /dev/null
+++ b/components/logo.tsx
@@ -0,0 +1,10 @@
+import { Icons } from "./icons";
+
+export function GameUnityLogo({ className }: { className?: string }) {
+    return (
+        <>
+            <Icons.logo className={`dark:hidden ${className}`} />
+            <Icons.logoWhite className={`hidden dark:block ${className}`} />
+        </>
+    )
+}
diff --git a/components/nav.tsx b/components/nav.tsx
index 45b8874..e4743b8 100644
--- a/components/nav.tsx
+++ b/components/nav.tsx
@@ -4,10 +4,11 @@ import { Icons, IconsType } from "@/components/icons";
 import { buttonVariants } from "@/components/ui/button";
 import { cn } from "@/lib/utils";
 import { SidebarNavItem } from "@/types";
+import { signIn, signOut, useSession } from "next-auth/react";
 import Link from "next/link";
 import { usePathname } from "next/navigation";
+import { GameUnityLogo } from "./logo";
 import { ModeToggle } from "./mode-toggle";
-import {signIn, signOut, useSession } from "next-auth/react"
 
 interface DashboardNavProps {
     items: SidebarNavItem[]
@@ -19,7 +20,7 @@ export default function DashboardNav({ items }: DashboardNavProps) {
     if (!items?.length) {
         return null
     }
-    
+
     const isLoaded = true
     const user = "test"
 
@@ -27,8 +28,7 @@ export default function DashboardNav({ items }: DashboardNavProps) {
         <nav className="grid items-start gap-2">
             <div className="flex items-center">
                 <Link href="/" className={cn("rounded-full p-3 hover:bg-accent")}>
-                    <Icons.logo className="h-7 w-7 dark:hidden" />
-                    <Icons.logoWhite className="h-7 w-7 hidden dark:block" />
+                    <GameUnityLogo className="h-8 w-8" />
                 </Link>
             </div>
             {session?.user && isLoaded && user ?
@@ -64,14 +64,14 @@ export default function DashboardNav({ items }: DashboardNavProps) {
                 </div>
             }
             {session?.user &&
-            <>
-            <p className="text-sky-600"> {session?.user.name}</p>
-            <button className=" text-red-500" onClick={() => signOut()}>
-                Sign Out
-            </button>
-            </>
+                <>
+                    <p className="text-sky-600"> {session?.user.name}</p>
+                    <button className=" text-red-500" onClick={() => signOut()}>
+                        Sign Out
+                    </button>
+                </>
             }
-        <ModeToggle />
+            <ModeToggle />
         </nav>
     )
 }
\ No newline at end of file
diff --git a/components/user-auth-form.tsx b/components/user-auth-form.tsx
new file mode 100644
index 0000000..9820124
--- /dev/null
+++ b/components/user-auth-form.tsx
@@ -0,0 +1,193 @@
+'use client'
+
+import { zodResolver } from "@hookform/resolvers/zod"
+import { signIn } from 'next-auth/react'
+import { useSearchParams } from 'next/navigation'
+import { HTMLAttributes, useState } from 'react'
+import { useForm } from 'react-hook-form'
+import * as z from "zod"
+
+import { Icons } from '@/components/icons'
+import { Button } from '@/components/ui/button'
+import { Input } from '@/components/ui/input'
+import { Label } from '@/components/ui/label'
+import { useToast } from '@/components/ui/use-toast'
+import { cn } from '@/lib/utils'
+import { userAuthSchema } from '@/lib/validations/auth'
+import { ToastAction } from "./ui/toast"
+
+interface UserAuthFormProps extends HTMLAttributes<HTMLDivElement> {
+    type: "login" | "signup"
+}
+
+type FormData = z.infer<typeof userAuthSchema>
+
+export function UserAuthForm({ type, className, ...props }: UserAuthFormProps) {
+    const {
+        register,
+        handleSubmit,
+        formState: { errors },
+    } = useForm<FormData>({
+        resolver: zodResolver(userAuthSchema),
+    })
+    const [isLoading, setIsLoading] = useState<boolean>(false)
+    const [isGitHubLoading, setIsGitHubLoading] = useState<boolean>(false)
+    const searchParams = useSearchParams()
+
+    const { toast } = useToast()
+
+    async function onSubmit(data: FormData) {
+        setIsLoading(true)
+
+        if (type === "signup") {
+            const res = await fetch('/api/signup', {
+                method: 'POST',
+                body: JSON.stringify({
+                    username: data.username,
+                    email: data.email,
+                    password: data.password
+                }),
+                headers: {
+                    'Content-Type': 'application/json'
+                }
+            })
+
+            if (!res.ok) {
+                setIsLoading(false)
+                toast({
+                    variant: "destructive",
+                    title: "Uh oh! Something went wrong.",
+                    description: "Your sign up request failed. Please try again.",
+                })
+            }
+        }
+
+        const signInResult = await signIn("credentials", {
+            username: data.username,
+            email: data.email,
+            password: data.password,
+            redirect: false,
+            callbackUrl: "/home",
+        });
+
+        setIsLoading(false)
+
+        if (!signInResult?.ok) {
+            toast({
+                variant: "destructive",
+                title: "Uh oh! Something went wrong.",
+                description: "Your log in request failed. Please try again.",
+                action: <ToastAction altText="Try again">Try again</ToastAction>,
+            })
+        }
+
+        // toast({
+        //     title: "Check your email.",
+        //     description: "We sent you a login link. Be sure to check your spam too.",
+        // })
+    }
+
+    return (
+        <>
+            <div className={cn("grid gap-6", className)} {...props}>
+                <form onSubmit={handleSubmit(onSubmit)}>
+                    <div className="grid gap-2">
+                        <div className="grid gap-1">
+                            <Label className="sr-only" htmlFor="username">
+                                Username
+                            </Label>
+                            <Input
+                                id="username"
+                                placeholder="Your username"
+                                type="username"
+                                autoCapitalize="none"
+                                autoComplete="username"
+                                autoCorrect="off"
+                                disabled={isLoading || isGitHubLoading}
+                                {...register("username")}
+                            />
+                            {errors?.username && (
+                                <p className="px-1 text-xs text-red-600">
+                                    {errors.username.message}
+                                </p>
+                            )}
+                        </div>
+                        {type === "signup" ? <div className="grid gap-1">
+                            <Label className="sr-only" htmlFor="email">
+                                Email
+                            </Label>
+                            <Input
+                                id="email"
+                                placeholder="Your email"
+                                type="email"
+                                autoCapitalize="none"
+                                autoComplete="email"
+                                autoCorrect="off"
+                                disabled={isLoading || isGitHubLoading}
+                                {...register("email")}
+                            />
+                            {errors?.email && (
+                                <p className="px-1 text-xs text-red-600">
+                                    {errors.email.message}
+                                </p>
+                            )}
+                        </div> : null}
+                        <div className="grid gap-1">
+                            <Label className="sr-only" htmlFor="password">
+                                Password
+                            </Label>
+                            <Input
+                                id="password"
+                                placeholder="Your password"
+                                type="password"
+                                autoCapitalize="none"
+                                autoComplete="new-password"
+                                autoCorrect="off"
+                                disabled={isLoading || isGitHubLoading}
+                                {...register("password")}
+                            />
+                            {errors?.password && (
+                                <p className="px-1 text-xs text-red-600">
+                                    {errors.password.message}
+                                </p>
+                            )}
+                        </div>
+                        <Button disabled={isLoading}>
+                            {isLoading && (
+                                <Icons.spinner className="mr-2 h-4 w-4 animate-spin" />
+                            )}
+                            {type === "signup" ? "Sign Up" : "Log In"}
+                        </Button>
+                    </div>
+                </form>
+
+                <div className="relative">
+                    <div className="absolute inset-0 flex items-center">
+                        <span className="w-full border-t" />
+                    </div>
+                    <div className="relative flex justify-center text-xs uppercase">
+                        <span className="bg-background px-2 text-muted-foreground">
+                            Or continue with
+                        </span>
+                    </div>
+                </div>
+                <Button
+                    variant="outline"
+                    type="button"
+                    onClick={() => {
+                        setIsGitHubLoading(true)
+                        signIn("github")
+                    }}
+                    disabled={isLoading || isGitHubLoading}
+                >
+                    {isGitHubLoading ? (
+                        <Icons.spinner className="mr-2 h-4 w-4 animate-spin" />
+                    ) : (
+                        <Icons.github className="mr-2 h-4 w-4" />
+                    )}{" "}
+                    Github
+                </Button>
+            </div>
+        </>
+    )
+}
\ No newline at end of file
diff --git a/components/user-item.tsx b/components/user-item.tsx
index 0da145d..64c729d 100644
--- a/components/user-item.tsx
+++ b/components/user-item.tsx
@@ -3,19 +3,19 @@ import Link from "next/link";
 import FollowButton from "./following-button";
 
 // this is a single user helper-component, only for design purposes
-export default function FollowUser({ id, followId, userName, image }: { id: number, followId: number, userName: string, image: { url: string } }) {
+export default function FollowUser({ id, followId, username, image }: { id: number, followId: number, username: string, image: { url: string } }) {
     return (
         <div>
             <Link href={`/user/${id}`}>
                 <div className="">
                     <Image
                         src={image.url}
-                        alt={userName}
+                        alt={username}
                         width={50}
                         height={50}
                         priority={true} />
                 </div>
-                <p>{userName}</p>
+                <p>{username}</p>
                 <FollowButton userId={id} followerId={followId} />
             </Link>
         </div>
diff --git a/env.mjs b/env.mjs
new file mode 100644
index 0000000..0154d5d
--- /dev/null
+++ b/env.mjs
@@ -0,0 +1,33 @@
+import { createEnv } from "@t3-oss/env-nextjs"
+import { z } from "zod"
+
+export const env = createEnv({
+    server: {
+        DATABASE_URL: z.string().min(1),
+        GITHUB_CLIENT_ID: z.string().min(1),
+        GITHUB_CLIENT_SECRET: z.string().min(1),
+        NEXTAUTH_URL: z.string().url().optional(),
+        NEXTAUTH_SECRET: z.string().min(1),
+        TWITCH_CLIENT_ID: z.string().min(1),
+        TWITCH_CLIENT_SECRET: z.string().min(1),
+        TWITCH_AUTH_BASE_URL: z.string().url().optional(),
+        IGDB_BASE_URL: z.string().url().optional(),
+        IGDB_IMG_BASE_URL: z.string().url().optional(),
+    },
+    client: {
+        NEXT_PUBLIC_APP_URL: z.string().min(1),
+    },
+    runtimeEnv: {
+        DATABASE_URL: process.env.DATABASE_URL,
+        GITHUB_CLIENT_ID: process.env.GITHUB_CLIENT_ID,
+        GITHUB_CLIENT_SECRET: process.env.GITHUB_CLIENT_SECRET,
+        NEXTAUTH_URL: process.env.NEXTAUTH_URL,
+        NEXTAUTH_SECRET: process.env.NEXTAUTH_SECRET,
+        NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL,
+        TWITCH_CLIENT_ID: process.env.TWITCH_CLIENT_ID,
+        TWITCH_CLIENT_SECRET: process.env.TWITCH_CLIENT_SECRET,
+        TWITCH_AUTH_BASE_URL: process.env.TWITCH_AUTH_BASE_URL,
+        IGDB_BASE_URL: process.env.IGDB_BASE_URL,
+        IGDB_IMG_BASE_URL: process.env.IGDB_IMG_BASE_URL,
+    },
+})
\ No newline at end of file
diff --git a/lib/auth.ts b/lib/auth.ts
index 2a10ac9..54f6453 100644
--- a/lib/auth.ts
+++ b/lib/auth.ts
@@ -1,29 +1,54 @@
+import { env } from "@/env.mjs"
+import { db } from "@/lib/db"
+import { PrismaAdapter } from "@auth/prisma-adapter"
 import { compare } from "bcrypt"
 import { NextAuthOptions } from "next-auth"
+import { Adapter } from "next-auth/adapters"
 import CredentialsProvider from 'next-auth/providers/credentials'
-import { prisma } from "./db"
+import GitHubProvider from "next-auth/providers/github"
 
 export const authOptions: NextAuthOptions = {
+    adapter: PrismaAdapter(db as any) as Adapter,
     session: {
         strategy: 'jwt'
     },
+    pages: {
+        signIn: "/login",
+    },
     providers: [
+        GitHubProvider({
+            clientId: env.GITHUB_CLIENT_ID as string,
+            clientSecret: env.GITHUB_CLIENT_SECRET as string,
+        }),
+
         CredentialsProvider({
-            name: 'Sign in',
+            name: 'Login',
             credentials: {
-                email: {
-                    label: 'Email',
-                    type: 'email',
-                    placeholder: 'hello@example.com'
-                },
+                username: { label: 'Username', type: 'text' },
+                email: { label: 'Email', type: 'email', placeholder: 'hello@example.com' },
                 password: { label: 'Password', type: 'password' }
             },
             async authorize(credentials) {
-                if (!credentials?.email || !credentials.password) {
+                if (!credentials?.username || !credentials.email || !credentials.password) {
                     return null
                 }
 
-                const user = await prisma.user.findUnique({
+                let isUnique = false;
+                while (!isUnique) {
+                    const existingUserName = await db.user.findUnique({
+                        where: {
+                            username: credentials.username
+                        }
+                    })
+
+                    if (existingUserName) {
+                        credentials.username = `${credentials.username}${Math.floor(Math.random() * 1000)}`
+                    } else {
+                        isUnique = true;
+                    }
+                }
+
+                const user = await db.user.findUnique({
                     where: {
                         email: credentials.email
                     }
@@ -43,34 +68,46 @@ export const authOptions: NextAuthOptions = {
                 }
 
                 return {
-                    id: user.id + '',
+                    id: user.id,
+                    username: user.username,
                     email: user.email,
                     name: user.name,
                 }
             }
         })
     ],
+    secret: env.NEXTAUTH_SECRET,
     callbacks: {
-        session: ({ session, token }) => {
-            console.log('Session Callback', { session, token })
-            return {
-                ...session,
-                user: {
-                    ...session.user,
-                    id: token.id,
-                }
+        async session({ token, session }) {
+            if (token) {
+                session.user.id = token.id + ''
+                session.user.name = token.name
+                session.user.email = token.email
+                session.user.image = token.picture
             }
+
+            return session
         },
-        jwt: ({ token, user }) => {
-            console.log('JWT Callback', { token, user })
-            if (user) {
-                const u = user as unknown as any
-                return {
-                    ...token,
-                    id: u.id,
+        async jwt({ token, user }) {
+            const dbUser = await db.user.findFirst({
+                where: {
+                    email: token.email,
+                },
+            })
+
+            if (!dbUser) {
+                if (user) {
+                    token.id = user?.id
                 }
+                return token
+            }
+
+            return {
+                id: dbUser.id,
+                name: dbUser.name,
+                email: dbUser.email,
+                picture: dbUser.image,
             }
-            return token
         }
     }
 }
\ No newline at end of file
diff --git a/lib/db.ts b/lib/db.ts
index e67728c..8a33a38 100644
--- a/lib/db.ts
+++ b/lib/db.ts
@@ -1,13 +1,18 @@
-import { PrismaClient } from '@prisma/client'
+import { PrismaClient } from "@prisma/client"
 
-const globalForPrisma = global as unknown as {
-  prisma: PrismaClient | undefined
+declare global {
+  // eslint-disable-next-line no-var
+  var cachedPrisma: PrismaClient
 }
 
-export const prisma =
-  globalForPrisma.prisma ??
-  new PrismaClient({
-    // log: ['query'],
-  })
+let prisma: PrismaClient
+if (process.env.NODE_ENV === "production") {
+  prisma = new PrismaClient()
+} else {
+  if (!global.cachedPrisma) {
+    global.cachedPrisma = new PrismaClient()
+  }
+  prisma = global.cachedPrisma
+}
 
-if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma
\ No newline at end of file
+export const db = prisma
\ No newline at end of file
diff --git a/lib/igdb.ts b/lib/igdb.ts
index d48e98e..93d0d43 100644
--- a/lib/igdb.ts
+++ b/lib/igdb.ts
@@ -1,11 +1,12 @@
+import { env } from "@/env.mjs"
 import { calculateOffset, getImageURL } from "@/lib/utils"
 import { IAuth, IGame } from "@/types/igdb-types"
 
-const TWITCH_AUTH_BASE_URL = process.env.TWITCH_AUTH_BASE_URL ?? ''
-const IGDB_BASE_URL = process.env.IGDB_BASE_URL ?? ''
+const TWITCH_AUTH_BASE_URL = env.TWITCH_AUTH_BASE_URL ?? ''
+const IGDB_BASE_URL = env.IGDB_BASE_URL ?? ''
 
-const CLIENT_ID = process.env.TWITCH_CLIENT_ID ?? ''
-const CLIENT_SECRET = process.env.TWITCH_CLIENT_SECRET ?? ''
+const CLIENT_ID = env.TWITCH_CLIENT_ID ?? ''
+const CLIENT_SECRET = env.TWITCH_CLIENT_SECRET ?? ''
 
 const limit = 100
 
diff --git a/lib/utils.ts b/lib/utils.ts
index 6a89161..3d7f8f6 100644
--- a/lib/utils.ts
+++ b/lib/utils.ts
@@ -1,3 +1,4 @@
+import { env } from "@/env.mjs"
 import { ClassValue, clsx } from "clsx"
 import { twMerge } from "tailwind-merge"
 
@@ -6,10 +7,9 @@ export function cn(...inputs: ClassValue[]) {
   return twMerge(clsx(inputs))
 }
 
-const IGDB_IMG_BASE_URL = process.env.IGDB_IMG_BASE_URL ?? ''
-
 // changes the default size of the image to be fetched
 export function getImageURL(hashId: string, size: string): string {
+  const IGDB_IMG_BASE_URL = env.IGDB_IMG_BASE_URL ?? ''
   return `${IGDB_IMG_BASE_URL}/t_${size}/${hashId}.jpg`
 }
 
diff --git a/lib/validations/auth.ts b/lib/validations/auth.ts
index e65b5cb..68de27a 100644
--- a/lib/validations/auth.ts
+++ b/lib/validations/auth.ts
@@ -1,5 +1,7 @@
 import * as z from "zod"
 
 export const userAuthSchema = z.object({
+    username: z.string().min(3).max(15),
     email: z.string().email(),
+    password: z.string().min(6).max(18),
 })
\ No newline at end of file
diff --git a/next.config.js b/next.config.mjs
similarity index 61%
rename from next.config.js
rename to next.config.mjs
index ce446f9..be988d3 100644
--- a/next.config.js
+++ b/next.config.mjs
@@ -1,9 +1,13 @@
+import "./env.mjs"
+
 /** @type {import('next').NextConfig} */
 const nextConfig = {
+    reactStrictMode: true,
+    swcMinify: true,
     images: {
         unoptimized: true,
         domains: ["images.igdb.com"]
     }
 }
 
-module.exports = nextConfig
+export default nextConfig
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index d7fdf5d..1e17dcd 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,6 +8,8 @@
       "name": "project_ss23_gameunity",
       "version": "0.2.0",
       "dependencies": {
+        "@auth/prisma-adapter": "^1.0.0",
+        "@hookform/resolvers": "^3.1.0",
         "@prisma/client": "^4.15.0",
         "@radix-ui/react-dropdown-menu": "^2.0.5",
         "@radix-ui/react-label": "^2.0.2",
@@ -15,6 +17,7 @@
         "@radix-ui/react-select": "^1.2.2",
         "@radix-ui/react-slot": "^1.0.2",
         "@radix-ui/react-toast": "^1.1.4",
+        "@t3-oss/env-nextjs": "^0.4.0",
         "@tanstack/react-query": "^4.29.12",
         "bcrypt": "^5.1.0",
         "class-variance-authority": "^0.6.0",
@@ -25,9 +28,11 @@
         "next-themes": "^0.2.1",
         "react": "18.2.0",
         "react-dom": "18.2.0",
+        "react-hook-form": "^7.44.3",
         "react-infinite-scroll-component": "^6.1.0",
         "tailwind-merge": "^1.12.0",
-        "tailwindcss-animate": "^1.0.5"
+        "tailwindcss-animate": "^1.0.5",
+        "zod": "^3.21.4"
       },
       "devDependencies": {
         "@tanstack/eslint-plugin-query": "^4.29.9",
@@ -55,6 +60,58 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/@auth/core": {
+      "version": "0.8.1",
+      "resolved": "https://registry.npmjs.org/@auth/core/-/core-0.8.1.tgz",
+      "integrity": "sha512-WudBmZudZ/cvykxHV5hIwrYsd7AlETQ535O7w3sSiiumT28+U9GvBb8oSRtfzxpW9rym3lAdfeTJqGA8U4FecQ==",
+      "dependencies": {
+        "@panva/hkdf": "^1.0.4",
+        "cookie": "0.5.0",
+        "jose": "^4.11.1",
+        "oauth4webapi": "^2.0.6",
+        "preact": "10.11.3",
+        "preact-render-to-string": "5.2.3"
+      },
+      "peerDependencies": {
+        "nodemailer": "^6.8.0"
+      },
+      "peerDependenciesMeta": {
+        "nodemailer": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@auth/core/node_modules/preact": {
+      "version": "10.11.3",
+      "resolved": "https://registry.npmjs.org/preact/-/preact-10.11.3.tgz",
+      "integrity": "sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==",
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/preact"
+      }
+    },
+    "node_modules/@auth/core/node_modules/preact-render-to-string": {
+      "version": "5.2.3",
+      "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.3.tgz",
+      "integrity": "sha512-aPDxUn5o3GhWdtJtW0svRC2SS/l8D9MAgo2+AWml+BhDImb27ALf04Q2d+AHqUUOc6RdSXFIBVa2gxzgMKgtZA==",
+      "dependencies": {
+        "pretty-format": "^3.8.0"
+      },
+      "peerDependencies": {
+        "preact": ">=10"
+      }
+    },
+    "node_modules/@auth/prisma-adapter": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/@auth/prisma-adapter/-/prisma-adapter-1.0.0.tgz",
+      "integrity": "sha512-+x+s5dgpNmqrcQC2ZRAXZIM6yhkWP/EXjIUgqUyMepLiX1OHi2AXIUAAbXsW4oG9OpYr/rvPIzPBpuGt6sPFwQ==",
+      "dependencies": {
+        "@auth/core": "0.8.1"
+      },
+      "peerDependencies": {
+        "@prisma/client": ">=2.26.0 || >=3 || >=4"
+      }
+    },
     "node_modules/@babel/runtime": {
       "version": "7.21.5",
       "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.5.tgz",
@@ -147,6 +204,14 @@
         "react-dom": ">=16.8.0"
       }
     },
+    "node_modules/@hookform/resolvers": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.1.0.tgz",
+      "integrity": "sha512-z0A8K+Nxq+f83Whm/ajlwE6VtQlp/yPHZnXw7XWVPIGm1Vx0QV8KThU3BpbBRfAZ7/dYqCKKBNnQh85BkmBKkA==",
+      "peerDependencies": {
+        "react-hook-form": "^7.0.0"
+      }
+    },
     "node_modules/@humanwhocodes/config-array": {
       "version": "0.11.8",
       "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz",
@@ -1210,6 +1275,27 @@
         "tslib": "^2.4.0"
       }
     },
+    "node_modules/@t3-oss/env-core": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/@t3-oss/env-core/-/env-core-0.4.0.tgz",
+      "integrity": "sha512-6JlMp0Vru15q/axHzBKsQQjiyGS6k+EsZBY1iErGVmOGzNSoVluBahnYFP7tEkwZ7KoRgSq4NRIc1Ez7SVYuxQ==",
+      "peerDependencies": {
+        "typescript": ">=4.7.2",
+        "zod": "^3.0.0"
+      }
+    },
+    "node_modules/@t3-oss/env-nextjs": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/@t3-oss/env-nextjs/-/env-nextjs-0.4.0.tgz",
+      "integrity": "sha512-K1u2i+S/uEhjfg++FqWlOzS6x237EARRbWGowH2MkDkFu2q7ZJSiJBJT8e47L7NHWH5IyZrTCM6BdOxyWEnQuQ==",
+      "dependencies": {
+        "@t3-oss/env-core": "0.4.0"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.7.2",
+        "zod": "^3.0.0"
+      }
+    },
     "node_modules/@tanstack/eslint-plugin-query": {
       "version": "4.29.9",
       "resolved": "https://registry.npmjs.org/@tanstack/eslint-plugin-query/-/eslint-plugin-query-4.29.9.tgz",
@@ -4341,6 +4427,14 @@
       "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz",
       "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA=="
     },
+    "node_modules/oauth4webapi": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-2.3.0.tgz",
+      "integrity": "sha512-JGkb5doGrwzVDuHwgrR4nHJayzN4h59VCed6EW8Tql6iHDfZIabCJvg6wtbn5q6pyB2hZruI3b77Nudvq7NmvA==",
+      "funding": {
+        "url": "https://github.com/sponsors/panva"
+      }
+    },
     "node_modules/object-assign": {
       "version": "4.1.1",
       "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -4909,6 +5003,21 @@
         "react": "^18.2.0"
       }
     },
+    "node_modules/react-hook-form": {
+      "version": "7.44.3",
+      "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.44.3.tgz",
+      "integrity": "sha512-/tHId6p2ViAka1wECMw8FEPn/oz/w226zehHrJyQ1oIzCBNMIJCaj6ZkQcv+MjDxYh9MWR7RQic7Qqwe4a5nkw==",
+      "engines": {
+        "node": ">=12.22.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/react-hook-form"
+      },
+      "peerDependencies": {
+        "react": "^16.8.0 || ^17 || ^18"
+      }
+    },
     "node_modules/react-infinite-scroll-component": {
       "version": "6.1.0",
       "resolved": "https://registry.npmjs.org/react-infinite-scroll-component/-/react-infinite-scroll-component-6.1.0.tgz",
@@ -5818,7 +5927,6 @@
       "version": "5.0.4",
       "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz",
       "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==",
-      "devOptional": true,
       "bin": {
         "tsc": "bin/tsc",
         "tsserver": "bin/tsserver"
diff --git a/package.json b/package.json
index 3c968f4..5dc490f 100644
--- a/package.json
+++ b/package.json
@@ -11,6 +11,8 @@
     "preview": "next build && next start"
   },
   "dependencies": {
+    "@auth/prisma-adapter": "^1.0.0",
+    "@hookform/resolvers": "^3.1.0",
     "@prisma/client": "^4.15.0",
     "@radix-ui/react-dropdown-menu": "^2.0.5",
     "@radix-ui/react-label": "^2.0.2",
@@ -18,6 +20,7 @@
     "@radix-ui/react-select": "^1.2.2",
     "@radix-ui/react-slot": "^1.0.2",
     "@radix-ui/react-toast": "^1.1.4",
+    "@t3-oss/env-nextjs": "^0.4.0",
     "@tanstack/react-query": "^4.29.12",
     "bcrypt": "^5.1.0",
     "class-variance-authority": "^0.6.0",
@@ -28,9 +31,11 @@
     "next-themes": "^0.2.1",
     "react": "18.2.0",
     "react-dom": "18.2.0",
+    "react-hook-form": "^7.44.3",
     "react-infinite-scroll-component": "^6.1.0",
     "tailwind-merge": "^1.12.0",
-    "tailwindcss-animate": "^1.0.5"
+    "tailwindcss-animate": "^1.0.5",
+    "zod": "^3.21.4"
   },
   "devDependencies": {
     "@tanstack/eslint-plugin-query": "^4.29.9",
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index dbf586a..70b33ff 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -10,15 +10,53 @@ datasource db {
   url      = env("DATABASE_URL")
 }
 
+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?
+  createdAt                DateTime @default(now()) @map(name: "created_at")
+  updatedAt                DateTime @default(now()) @map(name: "updated_at")
+  refresh_token_expires_in Int?
+
+  user User @relation(fields: [userId], references: [id], onDelete: Cascade)
+
+  @@unique([provider, providerAccountId])
+  @@map(name: "accounts")
+}
+
+model Session {
+  id           String   @id @default(cuid())
+  sessionToken String   @unique
+  userId       String
+  expires      DateTime
+
+  user User @relation(fields: [userId], references: [id], onDelete: Cascade)
+
+  @@map(name: "sessions")
+}
+
 model User {
-  id            Int       @id @default(autoincrement())
-  userName      String?   @unique 
-  name          String?   @default("u ${id}")
+  id            String    @id @default(cuid())
+  name          String?
+  username      String?   @unique @map("username")
   email         String?   @unique
-  password      String
   emailVerified DateTime?
-  image         String?   
-  createdAt     DateTime @default(now())
+  password      String
+  image         String?
+  createdAt     DateTime  @default(now()) @map(name: "created_at")
+  updatedAt     DateTime  @default(now()) @map(name: "updated_at")
+
+  accounts Account[]
+  sessions Session[]
 
   Post    Post[]
   Comment Comment[]
@@ -28,46 +66,67 @@ model User {
   following Follows[] @relation("following")
 }
 
+model VerificationToken {
+  identifier String
+  token      String   @unique
+  expires    DateTime
+
+  @@unique([identifier, token])
+  @@map(name: "verification_tokens")
+}
+
 model Follows {
   follower    User     @relation("following", fields: [followerId], references: [id])
-  followerId  Int
+  followerId  String
   following   User     @relation("follower", fields: [followingId], references: [id])
-  followingId Int
+  followingId String
   createdAt   DateTime @default(now())
 
   @@id([followerId, followingId])
+  @@map(name: "follows")
 }
 
 model Post {
-  id        Int      @id @default(autoincrement())
-  createdAt DateTime @default(now())
-  updatedAt DateTime @updatedAt
+  id        String   @id @default(cuid())
+  createdAt DateTime @default(now()) @map(name: "created_at")
+  updatedAt DateTime @default(now()) @map(name: "updated_at")
+  userId    String
   content   String
   likeCount Int?     @default(0)
-  gameId    Int?
   published Boolean  @default(false)
-  userId    Int
 
   user    User      @relation(fields: [userId], references: [id], onDelete: Cascade)
   Comment Comment[]
   Like    Like[]
+
+  @@map(name: "posts")
 }
 
 model Like {
-  id     Int @id @default(autoincrement())
-  postId Int
-  userId Int
+  id        String  @id @default(cuid())
+  postId    String
+  commentId String?
+  userId    String
 
-  post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
-  user User @relation(fields: [userId], references: [id], onDelete: Cascade)
+  post    Post     @relation(fields: [postId], references: [id], onDelete: Cascade)
+  comment Comment? @relation(fields: [commentId], references: [id], onDelete: Cascade)
+  user    User     @relation(fields: [userId], references: [id], onDelete: Cascade)
+
+  @@map(name: "likes")
 }
 
 model Comment {
-  id        Int      @id @default(autoincrement())
+  id        String   @id @default(cuid())
+  createdAt DateTime @default(now()) @map(name: "created_at")
+  updatedAt DateTime @default(now()) @map(name: "updated_at")
   message   String
-  postId    Int
-  userId    Int
-  createdAt DateTime @default(now())
-  post      Post     @relation(fields: [postId], references: [id], onDelete: Cascade)
-  user      User     @relation(fields: [userId], references: [id], onDelete: Cascade)
+  likeCount Int?     @default(0)
+  postId    String
+  userId    String
+
+  post Post   @relation(fields: [postId], references: [id], onDelete: Cascade)
+  user User   @relation(fields: [userId], references: [id], onDelete: Cascade)
+  Like Like[]
+
+  @@map(name: "comments")
 }
diff --git a/tsconfig.json b/tsconfig.json
index a914549..88c4dba 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -27,7 +27,8 @@
       "@/*": [
         "./*"
       ]
-    }
+    },
+    "strictNullChecks": true
   },
   "include": [
     "next-env.d.ts",
-- 
GitLab