From 9bc7278d7c69b3a49252c15dc6de446e0a0fce14 Mon Sep 17 00:00:00 2001 From: Deepesh Nair Date: Sat, 23 Sep 2023 10:35:18 +0530 Subject: [PATCH] Create POC for gamedoora UI (#86) * Added sign-in and sign-up pages * Github provider added * Changed AuthButton * Changed sign-up page to client * Fixed build issues * Added profile page --- hooks/useAuth.ts | 111 ---------- package-lock.json | 191 ++++++++++++++++++ package.json | 1 + pages/api/auth/me.ts | 46 ----- pages/api/auth/signin.ts | 76 ------- pages/api/auth/signup.ts | 107 ---------- public/gamedoora.png | Bin 0 -> 2431 bytes public/next.svg | 1 - public/vercel.svg | 1 - src/app/(auth)/components/Brand.tsx | 17 ++ src/app/(auth)/components/Hero.tsx | 32 +++ src/app/(auth)/components/SessionProvider.tsx | 3 + src/app/(auth)/login/page.tsx | 3 - src/app/(auth)/sign-in/page.tsx | 97 +++++++++ src/app/(auth)/sign-up/page.tsx | 114 ++++++++++- src/app/api/auth/[...nextauth]/auth.ts | 10 + src/app/api/auth/[...nextauth]/route.ts | 6 + src/app/components/AuthButton.tsx | 24 +++ src/app/components/Navbar.tsx | 31 ++- src/app/components/Sidebar.tsx | 24 ++- src/app/context/AuthContext.tsx | 84 -------- src/app/favicon.ico | Bin 25931 -> 25007 bytes src/app/layout.tsx | 16 +- src/app/page.tsx | 40 ++-- src/app/profile/[slug]/layout.tsx | 10 +- src/app/profile/[slug]/loading.tsx | 26 ++- src/app/profile/[slug]/page.tsx | 98 ++++++--- 27 files changed, 672 insertions(+), 497 deletions(-) delete mode 100644 hooks/useAuth.ts delete mode 100644 pages/api/auth/me.ts delete mode 100644 pages/api/auth/signin.ts delete mode 100644 pages/api/auth/signup.ts create mode 100644 public/gamedoora.png delete mode 100644 public/next.svg delete mode 100644 public/vercel.svg create mode 100644 src/app/(auth)/components/Brand.tsx create mode 100644 src/app/(auth)/components/Hero.tsx create mode 100644 src/app/(auth)/components/SessionProvider.tsx delete mode 100644 src/app/(auth)/login/page.tsx create mode 100644 src/app/(auth)/sign-in/page.tsx create mode 100644 src/app/api/auth/[...nextauth]/auth.ts create mode 100644 src/app/api/auth/[...nextauth]/route.ts create mode 100644 src/app/components/AuthButton.tsx delete mode 100644 src/app/context/AuthContext.tsx diff --git a/hooks/useAuth.ts b/hooks/useAuth.ts deleted file mode 100644 index 5419a8c..0000000 --- a/hooks/useAuth.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { AuthenticationContext } from '@/app/context/AuthContext'; -import axios from 'axios'; -import { deleteCookie } from 'cookies-next'; -import { useContext } from 'react'; - -const useAuth = () => { - const { setAuthState } = useContext(AuthenticationContext); - const signIn = async ( - { - email, - password, - }: { - email: string; - password: string; - }, - handleClose: () => void - ) => { - setAuthState({ - data: null, - error: null, - loading: true, - }); - try { - const response = await axios.post( - 'http://localhost:3000/api/auth/signin', - { - email, - password, - } - ); - setAuthState({ - data: response.data, - error: null, - loading: false, - }); - handleClose(); - } catch (error: any) { - setAuthState({ - data: null, - error: error.response.data.errorMessage, - loading: false, - }); - } - }; - const signUp = async ( - { - email, - password, - firstName, - lastName, - city, - phone, - }: { - email: string; - password: string; - firstName: string; - lastName: string; - city: string; - phone: string; - }, - handleClose: () => void - ) => { - setAuthState({ - data: null, - error: null, - loading: true, - }); - try { - const response = await axios.post( - 'http://localhost:3000/api/auth/signup', - { - email, - password, - firstName, - lastName, - city, - phone, - } - ); - setAuthState({ - data: response.data, - error: null, - loading: false, - }); - handleClose(); - } catch (error: any) { - setAuthState({ - data: null, - error: error.response.data.errorMessage, - loading: false, - }); - } - }; - - const signOut = () => { - deleteCookie('jwt'); - setAuthState({ - data: null, - loading: false, - error: null, - }); - }; - - return { - signIn, - signUp, - signOut, - }; -}; - -export default useAuth; diff --git a/package-lock.json b/package-lock.json index 7008b49..3ec822e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,7 @@ "jose": "^4.14.4", "jsonwebtoken": "^9.0.1", "next": "13.4.2", + "next-auth": "^4.23.1", "postcss": "8.4.23", "prisma": "^5.1.1", "react": "18.2.0", @@ -1001,6 +1002,14 @@ "node": ">= 8" } }, + "node_modules/@panva/hkdf": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.1.1.tgz", + "integrity": "sha512-dhPeilub1NuIG0X5Kvhh9lH4iW3ZsHlnzwgwbOlgwQ2wG1IqFzsgHqmKPk3WzsdWAeaxKJxgM0+W433RmN45GA==", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/@pkgr/utils": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.0.tgz", @@ -4144,6 +4153,41 @@ } } }, + "node_modules/next-auth": { + "version": "4.23.1", + "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.23.1.tgz", + "integrity": "sha512-mL083z8KgRtlrIV6CDca2H1kduWJuK/3pTS0Fe2og15KOm4v2kkLGdSDfc2g+019aEBrJUT0pPW2Xx42ImN1WA==", + "dependencies": { + "@babel/runtime": "^7.20.13", + "@panva/hkdf": "^1.0.2", + "cookie": "^0.5.0", + "jose": "^4.11.4", + "oauth": "^0.9.15", + "openid-client": "^5.4.0", + "preact": "^10.6.3", + "preact-render-to-string": "^5.1.19", + "uuid": "^8.3.2" + }, + "peerDependencies": { + "next": "^12.2.5 || ^13", + "nodemailer": "^6.6.5", + "react": "^17.0.2 || ^18", + "react-dom": "^17.0.2 || ^18" + }, + "peerDependenciesMeta": { + "nodemailer": { + "optional": true + } + } + }, + "node_modules/next-auth/node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/next/node_modules/postcss": { "version": "8.4.14", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", @@ -4262,6 +4306,11 @@ "set-blocking": "^2.0.0" } }, + "node_modules/oauth": { + "version": "0.9.15", + "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", + "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==" + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -4383,6 +4432,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/oidc-token-hash": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.3.tgz", + "integrity": "sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==", + "engines": { + "node": "^10.13.0 || >=12.0.0" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -4422,6 +4479,28 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/openid-client": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.5.0.tgz", + "integrity": "sha512-Y7Xl8BgsrkzWLHkVDYuroM67hi96xITyEDSkmWaGUiNX6CkcXC3XyQGdv5aWZ6dukVKBFVQCADi9gCavOmU14w==", + "dependencies": { + "jose": "^4.14.4", + "lru-cache": "^6.0.0", + "object-hash": "^2.2.0", + "oidc-token-hash": "^5.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/openid-client/node_modules/object-hash": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", + "engines": { + "node": ">= 6" + } + }, "node_modules/optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -4689,6 +4768,26 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, + "node_modules/preact": { + "version": "10.17.1", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.17.1.tgz", + "integrity": "sha512-X9BODrvQ4Ekwv9GURm9AKAGaomqXmip7NQTZgY7gcNmr7XE83adOMJvd3N42id1tMFU7ojiynRsYnY6/BRFxLA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/preact-render-to-string": { + "version": "5.2.6", + "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz", + "integrity": "sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==", + "dependencies": { + "pretty-format": "^3.8.0" + }, + "peerDependencies": { + "preact": ">=10" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -4697,6 +4796,11 @@ "node": ">= 0.8.0" } }, + "node_modules/pretty-format": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz", + "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==" + }, "node_modules/prisma": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.1.1.tgz", @@ -5668,6 +5772,14 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/validator": { "version": "13.11.0", "resolved": "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz", @@ -6452,6 +6564,11 @@ "fastq": "^1.6.0" } }, + "@panva/hkdf": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.1.1.tgz", + "integrity": "sha512-dhPeilub1NuIG0X5Kvhh9lH4iW3ZsHlnzwgwbOlgwQ2wG1IqFzsgHqmKPk3WzsdWAeaxKJxgM0+W433RmN45GA==" + }, "@pkgr/utils": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.0.tgz", @@ -8680,6 +8797,29 @@ } } }, + "next-auth": { + "version": "4.23.1", + "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.23.1.tgz", + "integrity": "sha512-mL083z8KgRtlrIV6CDca2H1kduWJuK/3pTS0Fe2og15KOm4v2kkLGdSDfc2g+019aEBrJUT0pPW2Xx42ImN1WA==", + "requires": { + "@babel/runtime": "^7.20.13", + "@panva/hkdf": "^1.0.2", + "cookie": "^0.5.0", + "jose": "^4.11.4", + "oauth": "^0.9.15", + "openid-client": "^5.4.0", + "preact": "^10.6.3", + "preact-render-to-string": "^5.1.19", + "uuid": "^8.3.2" + }, + "dependencies": { + "cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" + } + } + }, "node-addon-api": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", @@ -8742,6 +8882,11 @@ "set-blocking": "^2.0.0" } }, + "oauth": { + "version": "0.9.15", + "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", + "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==" + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -8821,6 +8966,11 @@ "es-abstract": "^1.20.4" } }, + "oidc-token-hash": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.3.tgz", + "integrity": "sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==" + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -8848,6 +8998,24 @@ "is-wsl": "^2.2.0" } }, + "openid-client": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.5.0.tgz", + "integrity": "sha512-Y7Xl8BgsrkzWLHkVDYuroM67hi96xITyEDSkmWaGUiNX6CkcXC3XyQGdv5aWZ6dukVKBFVQCADi9gCavOmU14w==", + "requires": { + "jose": "^4.14.4", + "lru-cache": "^6.0.0", + "object-hash": "^2.2.0", + "oidc-token-hash": "^5.0.3" + }, + "dependencies": { + "object-hash": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==" + } + } + }, "optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -9002,11 +9170,29 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, + "preact": { + "version": "10.17.1", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.17.1.tgz", + "integrity": "sha512-X9BODrvQ4Ekwv9GURm9AKAGaomqXmip7NQTZgY7gcNmr7XE83adOMJvd3N42id1tMFU7ojiynRsYnY6/BRFxLA==" + }, + "preact-render-to-string": { + "version": "5.2.6", + "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz", + "integrity": "sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==", + "requires": { + "pretty-format": "^3.8.0" + } + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" }, + "pretty-format": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz", + "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==" + }, "prisma": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.1.1.tgz", @@ -9665,6 +9851,11 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, "validator": { "version": "13.11.0", "resolved": "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz", diff --git a/package.json b/package.json index 77b50b3..276682b 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "jose": "^4.14.4", "jsonwebtoken": "^9.0.1", "next": "13.4.2", + "next-auth": "^4.23.1", "postcss": "8.4.23", "prisma": "^5.1.1", "react": "18.2.0", diff --git a/pages/api/auth/me.ts b/pages/api/auth/me.ts deleted file mode 100644 index e4cea32..0000000 --- a/pages/api/auth/me.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { NextApiRequest, NextApiResponse } from 'next'; - -import jwt from 'jsonwebtoken'; -import { PrismaClient } from '@prisma/client'; - -const prisma = new PrismaClient(); - -export default async function handler( - req: NextApiRequest, - res: NextApiResponse -) { - const bearerToken = req.headers['authorization'] as string; - const token = bearerToken.split(' ')[1]; - - const payload = jwt.decode(token) as { email: string }; - - if (!payload.email) { - return res.status(401).send({ - errorMessage: 'Unauthorized request', - }); - } - - const user = await prisma.user.findUnique({ - where: { - email: payload.email, - }, - select: { - id: true, - name: true, - email: true, - avatar: true, - phone: true, - userID: true, - }, - }); - - if (!user) { - return res.status(401).json({ - errorMessage: 'User not found', - }); - } - - return res.json({ - me: user, - }); -} diff --git a/pages/api/auth/signin.ts b/pages/api/auth/signin.ts deleted file mode 100644 index 0a0c828..0000000 --- a/pages/api/auth/signin.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { PrismaClient } from '@prisma/client'; -import { NextApiRequest, NextApiResponse } from 'next'; -import validator from 'validator'; -import bcrypt from 'bcrypt'; -import * as jose from 'jose'; -import { setCookie } from 'cookies-next'; - -const prisma = new PrismaClient(); - -export default async function handler( - req: NextApiRequest, - res: NextApiResponse -) { - if (req.method === 'POST') { - const { email, password } = req.body; - const errors: string[] = []; - const validationSchema = [ - { - valid: validator.isEmail(email), - errorMessage: 'Invalid email/User does not exists', - }, - { - valid: validator.isLength(password, { - min: 1, - }), - errorMessage: 'Password is invalid', - }, - ]; - validationSchema.forEach((check) => { - if (!check.valid) { - errors.push(check.errorMessage); - } - }); - - if (errors.length > 0) { - return res.status(400).json({ errorMessage: errors[0] }); - } - const user = await prisma.user.findUnique({ - where: { email }, - }); - - if (!user) { - return res - .status(401) - .json({ errorMessage: 'Email or password invalid ' }); - } - const isMatch = await bcrypt.compare(password, user.password); - if (!isMatch) { - return res - .status(401) - .json({ errorMessage: 'Email or password invalid ' }); - } - - const alg = 'HS256'; - const secret = new TextEncoder().encode(process.env.JWT_SECRET); - const token = await new jose.SignJWT({ - email: user.email, - }) - .setProtectedHeader({ alg }) - .setExpirationTime('24h') - .sign(secret); - setCookie('jwt', token, { - req, - res, - maxAge: 60 * 6 * 24, - }); - return res.status(200).json({ - name: user.name, - email: user.email, - phone: user.phone, - avatar: user.avatar, - }); - } - - return res.status(404).json('Undefined Endpoint'); -} diff --git a/pages/api/auth/signup.ts b/pages/api/auth/signup.ts deleted file mode 100644 index e7c30d0..0000000 --- a/pages/api/auth/signup.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { PrismaClient } from '@prisma/client'; -import { NextApiRequest, NextApiResponse } from 'next'; -import validator from 'validator'; -import bcrypt from 'bcrypt'; -import * as jose from 'jose'; -import { setCookie } from 'cookies-next'; - -const prisma = new PrismaClient(); - -export default async function handler( - req: NextApiRequest, - res: NextApiResponse -) { - if (req.method === 'POST') { - const { name, userID, email, password, phone, avatar } = req.body; - const errors: string[] = []; - const validationSchema = [ - { - valid: validator.isLength(name, { - min: 1, - max: 50, - }), - errorMessage: 'Name is too long/invalid', - }, - { - valid: validator.isLength(userID, { - min: 1, - max: 50, - }), - errorMessage: 'Name is too long/invalid', - }, - { - valid: validator.isEmail(email), - errorMessage: 'Incorrect email', - }, - { - valid: validator.isMobilePhone(phone), - errorMessage: 'Phone number is invalid', - }, - { - valid: validator.isStrongPassword(password), - errorMessage: 'Password is not strong', - }, - ]; - validationSchema.forEach((check) => { - if (!check.valid) { - errors.push(check.errorMessage); - } - }); - if (errors.length > 0) { - return res.status(400).json({ - errorMessage: errors[0], - }); - } - - const userWithEmail = await prisma.user.findUnique({ - where: { email }, - }); - if (userWithEmail) { - return res.status(400).json({ - errorMessage: 'Email already exists', - }); - } - - const userWithUserID = await prisma.user.findUnique({ - where: { userID }, - }); - if (userWithEmail) { - return res.status(400).json({ - errorMessage: 'UserID taken', - }); - } - - const hashedPassword = await bcrypt.hash(password, 10); - const user = await prisma.user.create({ - data: { - name, - password: hashedPassword, - phone, - email, - userID, - avatar, - }, - }); - const alg = 'HS256'; - const secret = new TextEncoder().encode(process.env.JWT_SECRET); - const token = await new jose.SignJWT({ - email: user.email, - userID: user.userID, - }) - .setProtectedHeader({ alg }) - .setExpirationTime('24h') - .sign(secret); - setCookie('jwt', token, { - req, - res, - maxAge: 60 * 6 * 24, - }); - return res.status(200).json({ - name: user.name, - email: user.email, - phone: user.phone, - userID: user.userID, - avatar: user.avatar, - }); - } -} diff --git a/public/gamedoora.png b/public/gamedoora.png new file mode 100644 index 0000000000000000000000000000000000000000..5597e5ad0f8ccd6cc47dd9ebba4cd6be1c646275 GIT binary patch literal 2431 zcmV-_34r#AP)1%By`hcdWh^1YdSZR~^c^~IY=iYPO8`lX*>b2^` zQGPnt_O<=}{Lc4x?$5aruJZ8>z?B|XJ%HjIJhdKp{(gX&0EqPm#sE233v)IBPpkt< zD2~jRgCME`rcXoi*MO!bAO#xXjkP0lYA0wqi^%E?ZQ-uR9@_fhSHF7U#os;<3i=NM zI9Uq8XU?9r!Wku70mc{@^&oKVJ&*h9I`?G9{&_P!xebcPUkPA0W9;5ieV2Qb&O$~S zq3bul7KyL?gI79VZf25FS^rU5+# zrlkQ>=LC=>35^X6kY)KdjIkF=A>0|448WKKZ^La*L~24$D)c;{%{l;#2>|PhEscw8y~qbB6?rW=CQ6|czV6rj%nS_;sTV0<=oYcp^lh=7nJ>2(0{D+<7OE2*sh zhVZo;hJ5n*kd=8YX=NW;dX9x02qGYAs;gs^(m?=CR|J6RfTymzJrr*r3YJYrkrUS8 z00M$(8DSxb7qS%&gx~K+WmQ$PfLM810T|Vx)?VKcsHzjcVtXwG|nlBFk&!)GcGY}2!2!&D+OT6xgPXk8E7OrK%v@Ae{ zg+%uAHsTPBp*$Ss5KWZQ9ROOE1E4JBWK+*{hMQLH2nD7i$YcPMT3P1BYK19|DZsQG zV}_c~kh2mz`-ETOT@WCoX)=-6AxqMF0AG|IfB;73TvsS4eN-MI5oEHqcxnQMX+!87 zKpZn-??C)@X9Wr@GzX%*oWBwm5WP!*SP+20ZitA#sw)^R`zRdA+Ttk_*{Ak)r1LNY z0JWUSbRCSt2ms<1&Oeqz@y`}*h=5oznMe!@h!cweSg;1TV;QACtEda?jD(XBq|e!2 z&)Hk0Rj|1Xkjq-Who%9#X0HYhN?o6YqP5^0TM(fT8tUpAi6rf1jBUQ86<|yvk}vY7 zSX_NXCDTBLiOlnWEzT5=b!xAOX|D`rCiFBV++L<>Qc9_{|3hSd-oY5WJ|E(ZjTPgk zPyf--^-e;_v&#UGh~z1II~JEWsf_RIunN->$mcwW2!ma)6@Y^y2DB;qx2;pv5x;Cc z>(l_|D`s)bZRO03_4OD(efk+g*Cj&8)AIoowGfe&XJSq2CY7Z`aT<1UInA zG-DU1Rjfj!WfMoZC}Kmb0w_)#V>t7L@!HmG^Oh62z+2H^+1gkvX8GF@?FLyCgxvtX z3ZQKfDX@I0>tN-_Q=!^y+u`y*z{|HobGJrpSPdWG7O*YqkDF1 z^_$+AS@B|!m`}&!aogK&K?N3CEQHVNMRiqGO)i%kG)=R!C_rik7#%d_^!apwZ3_JE z*oud4ho`I>{Xe*IxOx4J>F+=CsQT#U&Cqpyj_^6hMOK6VUf?^GRB+5^${1E%a}9p| z(o3YHqeCwWAQ9{}G~74*E^;|d>^eLQ5vB+#X2bP3IRmBTuIGN(seEt!x6EJGHpJg< zZ*Paeo4Exb^1>Hd2|^GKhwiKniZJp&y3dqApM{eH#APX|EcyLx(hCIZ2r{kvG$K`QJ{3!t2kxpgST zz`y`1D=U9W2r1eh6}=9~GG01y1RosPFV0{^gu4Mu@y78{;LPVMeZksy?@0f3JPYpw z{r!C=7lQ(!vIt3*=LjbPmW#h{U;y!^rY$C=?-bJskz;GL*VrNc$-rdbFH;l9PEFuD zRWT?ED_99yWL5Y$a~$wTX%I7w5*v~>jdTJehl zfL++T8AJc@5R%EHORQrf_hWY9c@7{eG9*QTq9~S7YvvG&0#yVbt!-`C(BFSVx5ZB^ zM%SVN1jL91u?w^Lm7oQ|=FZ<&N-hD=Co-@mK|XV`nFBx#gBUxm>P?0EwmOSu%it z*awF7!+S9^n-)(NW?xjPBunscv0S*Axo{5`FG***8fPgp%VBPQmoX_Mg z77wv+`+Polgm6Cd?6vs zc9{Rx_s>jE_vxCJ=DiNzLQSXfyPN>9uM?x^X7Jph&)wH`@tQ=ej9aVV z@gNe3ptrX-Nh#e7AUF5q<;XA59U57}>*Eu6^U!B=A$XyBynzll;hvb \ No newline at end of file diff --git a/public/vercel.svg b/public/vercel.svg deleted file mode 100644 index d2f8422..0000000 --- a/public/vercel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/app/(auth)/components/Brand.tsx b/src/app/(auth)/components/Brand.tsx new file mode 100644 index 0000000..fbc2ebc --- /dev/null +++ b/src/app/(auth)/components/Brand.tsx @@ -0,0 +1,17 @@ +import Image from 'next/image'; + +export default function Brand({ signIn }: { signIn: boolean }) { + return ( +
+

+ Gamedoora + Gamedoora +

+

+ {signIn + ? 'Sign In to access your account' + : 'Sign Up to create an account'} +

+
+ ); +} diff --git a/src/app/(auth)/components/Hero.tsx b/src/app/(auth)/components/Hero.tsx new file mode 100644 index 0000000..55614ce --- /dev/null +++ b/src/app/(auth)/components/Hero.tsx @@ -0,0 +1,32 @@ +import Image from 'next/image'; +import Link from 'next/link'; + +export default function Hero() { + return ( +
+
+
+ + Gamedoora +

Gamedoora

+ + +

+ A platform for passionate people to connect and collaborate freely. +

+
+
+
+ ); +} diff --git a/src/app/(auth)/components/SessionProvider.tsx b/src/app/(auth)/components/SessionProvider.tsx new file mode 100644 index 0000000..da59d85 --- /dev/null +++ b/src/app/(auth)/components/SessionProvider.tsx @@ -0,0 +1,3 @@ +'use client'; +import { SessionProvider } from "next-auth/react"; +export default SessionProvider; diff --git a/src/app/(auth)/login/page.tsx b/src/app/(auth)/login/page.tsx deleted file mode 100644 index d07a977..0000000 --- a/src/app/(auth)/login/page.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function Login() { - return
Login
; -} diff --git a/src/app/(auth)/sign-in/page.tsx b/src/app/(auth)/sign-in/page.tsx new file mode 100644 index 0000000..e50e8ae --- /dev/null +++ b/src/app/(auth)/sign-in/page.tsx @@ -0,0 +1,97 @@ +'use client'; +import Link from 'next/link'; +import Hero from '../components/Hero'; +import Brand from '../components/Brand'; +import { signIn, useSession } from 'next-auth/react'; +import { redirect } from 'next/navigation'; + +export default function SignIn() { + const { data: session } = useSession(); + if (session) { + redirect(`/`); + } + return ( +
+
+ + +
+
+ + +
+
+
+ + +
+ +
+
+ + + Forgot password? + +
+ + +
+ +
+ +
+
+ +
+ +
+ +

+ Don't have an account yet?{' '} + + Sign up + + . +

+
+
+
+
+
+ ); +} diff --git a/src/app/(auth)/sign-up/page.tsx b/src/app/(auth)/sign-up/page.tsx index e36cc7d..13a2e57 100644 --- a/src/app/(auth)/sign-up/page.tsx +++ b/src/app/(auth)/sign-up/page.tsx @@ -1,3 +1,115 @@ +'use client'; +import Link from 'next/link'; +import Hero from '../components/Hero'; +import Brand from '../components/Brand'; +import { useSession } from 'next-auth/react'; +import { redirect } from 'next/navigation'; + export default function SignUp() { - return
SignUp
; + const { data: session } = useSession(); + if (session) { + redirect(`/`); + } + return ( +
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+ +
+ + +
+ +
+
+ +
+ + +
+ +
+ +
+
+ +

+ Have an account already?{' '} + + Sign In + + . +

+
+
+
+
+
+ ); } diff --git a/src/app/api/auth/[...nextauth]/auth.ts b/src/app/api/auth/[...nextauth]/auth.ts new file mode 100644 index 0000000..5b4b977 --- /dev/null +++ b/src/app/api/auth/[...nextauth]/auth.ts @@ -0,0 +1,10 @@ +import { NextAuthOptions } from 'next-auth'; +import GitHubProvider from 'next-auth/providers/github'; +export const authOptions: NextAuthOptions = { + providers: [ + GitHubProvider({ + clientId: process.env.GITHUB_ID ?? '', + clientSecret: process.env.GITHUB_SECRET ?? '', + }), + ], +}; \ No newline at end of file diff --git a/src/app/api/auth/[...nextauth]/route.ts b/src/app/api/auth/[...nextauth]/route.ts new file mode 100644 index 0000000..5cccc51 --- /dev/null +++ b/src/app/api/auth/[...nextauth]/route.ts @@ -0,0 +1,6 @@ +import NextAuth from "next-auth/next"; +import { authOptions } from "./auth"; + +const handler = NextAuth(authOptions); + +export { handler as GET, handler as POST }; diff --git a/src/app/components/AuthButton.tsx b/src/app/components/AuthButton.tsx new file mode 100644 index 0000000..67af817 --- /dev/null +++ b/src/app/components/AuthButton.tsx @@ -0,0 +1,24 @@ +'use client'; +import { signOut, useSession, signIn } from 'next-auth/react'; + +export default function AuthButton() { + const { data: session } = useSession(); + if (session) { + return ( + + ); + } + return ( + + ); +} diff --git a/src/app/components/Navbar.tsx b/src/app/components/Navbar.tsx index f1e68f1..dcd0bda 100644 --- a/src/app/components/Navbar.tsx +++ b/src/app/components/Navbar.tsx @@ -1,7 +1,36 @@ +'use client'; +import Link from 'next/link'; +import AuthButton from './AuthButton'; + /** * This is the global navbar which would be available on all screens expect the login * @returns Global navbar */ export default function Navbar() { - return
This is a demo navbar
; + return ( +
+ +
+ ); } diff --git a/src/app/components/Sidebar.tsx b/src/app/components/Sidebar.tsx index a6c80aa..99ad6c3 100644 --- a/src/app/components/Sidebar.tsx +++ b/src/app/components/Sidebar.tsx @@ -1,10 +1,24 @@ -import Link from "next/link"; +import Image from 'next/image'; +import Link from 'next/link'; export default function Sidebar() { return ( - + ); } diff --git a/src/app/context/AuthContext.tsx b/src/app/context/AuthContext.tsx deleted file mode 100644 index fb69d5e..0000000 --- a/src/app/context/AuthContext.tsx +++ /dev/null @@ -1,84 +0,0 @@ -'use client'; - -import { useState, createContext, useEffect } from 'react'; -import { getCookie } from 'cookies-next'; -import axios from 'axios'; - -interface User { - id: number; - name: string; - email: string; -} - -interface State { - loading: boolean; - error: string | null; - data: User | null; -} - -interface AuthState extends State { - setAuthState: React.Dispatch>; -} - -export const AuthenticationContext = createContext({ - loading: false, - error: null, - data: null, - setAuthState: () => {}, -}); - -export default function AuthContext({ - children, -}: { - children: React.ReactNode; -}) { - const [authState, setAuthState] = useState({ - loading: true, - data: null, - error: null, - }); - - const fetchUser = async () => { - try { - const jwt = getCookie('jwt'); - if (!jwt) { - return setAuthState({ - data: null, - error: null, - loading: false, - }); - } - const response = await axios.get('http://localhost:3000/api/login', { - headers: { - Authorization: `Bearer ${jwt}`, - }, - }); - axios.defaults.headers.common['Authorization'] = `Bearer ${jwt}`; - setAuthState({ - data: response.data, - error: null, - loading: false, - }); - } catch (error: any) { - setAuthState({ - data: null, - error: error.response.data.errorMessage, - loading: false, - }); - } - }; - - useEffect(() => { - fetchUser(); - }, []); - return ( - - {children} - - ); -} diff --git a/src/app/favicon.ico b/src/app/favicon.ico index 718d6fea4835ec2d246af9800eddb7ffb276240c..3339389feda04883c91c04226d4357e4ecf641f9 100644 GIT binary patch literal 25007 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 LrJugUA?W`A8`#=m diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 7cf0a8d..1af1041 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,7 +1,10 @@ import ReactQueryProvider from './components/ReactQueryProvider'; -import AuthContext from './context/AuthContext'; import './globals.css'; import { Inter } from 'next/font/google'; +import { getServerSession } from 'next-auth'; +import SessionProvider from './(auth)/components/SessionProvider'; +import Sidebar from './components/Sidebar'; +import Navbar from './components/Navbar'; const inter = Inter({ subsets: ['latin'] }); @@ -10,20 +13,19 @@ export const metadata = { description: 'Gamedoora is here to revolutionize the gaming industry', }; -export default function RootLayout({ +export default async function RootLayout({ children, }: { children: React.ReactNode; }) { + const session = await getServerSession(); return (
- - -
{children}
-
-
+ + {children} +
diff --git a/src/app/page.tsx b/src/app/page.tsx index 58e5bf0..ac378f4 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,30 +1,32 @@ 'use client'; -import Link from 'next/link'; import { useFetchFeed } from '../../services/feed'; import { TFeed } from '../../services/feed/type'; +import { useSession } from 'next-auth/react'; +import { redirect } from 'next/navigation'; +import Sidebar from './components/Sidebar'; +import Navbar from './components/Navbar'; export default function Home() { const { data, isError, isLoading } = useFetchFeed('hybridx'); + const { data: session } = useSession(); + if (!session) { + redirect('/sign-in'); + } return ( -
- - Goto login - - - Goto Sign-up - - - Goto {`Hybridx's`} profile - -
The following is some feed data
-
- {data?.feed.map((f: TFeed) => ( -
-

{f.title}

-

{f.author}

-
- ))} +
+ +
+ +
+
The following is some feed data
+ {data?.feed.map((f: TFeed) => ( +
+

{f.title}

+

{f.author}

+
+ ))} +
); diff --git a/src/app/profile/[slug]/layout.tsx b/src/app/profile/[slug]/layout.tsx index de24f91..ec7587a 100644 --- a/src/app/profile/[slug]/layout.tsx +++ b/src/app/profile/[slug]/layout.tsx @@ -1,4 +1,5 @@ import Navbar from '@/app/components/Navbar'; +import Sidebar from '@/app/components/Sidebar'; export default function ProfileLayout({ children, @@ -10,9 +11,12 @@ export default function ProfileLayout({ }; }) { return ( -
- - {children} +
+ +
+ + {children} +
); } diff --git a/src/app/profile/[slug]/loading.tsx b/src/app/profile/[slug]/loading.tsx index 898336f..25a910a 100644 --- a/src/app/profile/[slug]/loading.tsx +++ b/src/app/profile/[slug]/loading.tsx @@ -1,3 +1,27 @@ export default function Loading() { - return
You project page is loading...
; + return ( +
+ {' '} +
+ {/* Profile Header Skeleton */} +
+
+
+
+
+ + {/* About Me Skeleton */} +
+
+
+
+ + {/* Contact Information Skeleton */} +
+
+
+
+
+
+ ); } diff --git a/src/app/profile/[slug]/page.tsx b/src/app/profile/[slug]/page.tsx index fe333e7..9785add 100644 --- a/src/app/profile/[slug]/page.tsx +++ b/src/app/profile/[slug]/page.tsx @@ -1,6 +1,7 @@ /* eslint-disable @next/next/no-img-element */ -import { PrismaClient, User } from '@prisma/client'; +import { PrismaClient } from '@prisma/client'; import { notFound } from 'next/navigation'; +import { getServerSession } from 'next-auth'; const prisma = new PrismaClient(); @@ -12,24 +13,24 @@ interface IUser { phone: string; } -const fetchUserByUser = async (slug: string): Promise => { - const user = await prisma.user.findUnique({ - where: { - userID: slug, - }, - select: { - name: true, - userID: true, - avatar: true, - email: true, - phone: true, - }, - }); - if (!user) { - notFound(); - } - return user; -}; +// const fetchUserByUser = async (slug: string): Promise => { +// const user = await prisma.user.findUnique({ +// where: { +// userID: slug, +// }, +// select: { +// name: true, +// userID: true, +// avatar: true, +// email: true, +// phone: true, +// }, +// }); +// if (!user) { +// notFound(); +// } +// return user; +// }; export default async function Profile({ params, @@ -38,19 +39,54 @@ export default async function Profile({ slug: string; }; }) { - const user = await fetchUserByUser(params.slug); + // const user = await fetchUserByUser(params.slug); + const session = await getServerSession(); return ( -
-
- -
-
{user.name}
-
{user.email}
-
{user.phone}
+ <> + {session?.user?.name ? ( +
+
+
+ User Photo +
+

+ {session?.user?.name} +

+

Game developer

+
+
+
+

About Me

+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla + vitae hendrerit ante. Vivamus semper, purus id fermentum + posuere. +

+
+
+

+ Contact Information +

+

+ Email:{' '} + + {session?.user?.email} + +
+ Phone: +91 9876543210 +
+ Location: Bharat +

+
+
-
-
+ ) : ( + 'Not logged in' + )} + ); }