diff --git a/src/pages/auth.tsx b/src/pages/auth.tsx index ca1f0ba..7b9af97 100644 --- a/src/pages/auth.tsx +++ b/src/pages/auth.tsx @@ -2,8 +2,9 @@ import { useState } from 'react' import { useAuth } from 'hooks/useAuth' import { fetchData } from 'services/fetchData' +import { guestUserRoute } from 'utils/guestUserRoute' -import type { GetStaticProps } from 'next' +import type { GetServerSideProps } from 'next' export default function AuthPage() { const { signIn } = useAuth() @@ -36,7 +37,7 @@ export default function AuthPage() { ) } -export const getStaticProps: GetStaticProps = async () => { +export const getServerSideProps: GetServerSideProps = guestUserRoute(async () => { const initialData = await fetchData() - return { props: { initialData }, revalidate: 1 } -} + return { props: { initialData } } +}) diff --git a/src/pages/dash.tsx b/src/pages/dash.tsx index 2888e2b..9ab7ae7 100644 --- a/src/pages/dash.tsx +++ b/src/pages/dash.tsx @@ -1,5 +1,6 @@ import { Dash } from 'components/Dash' import { fetchData } from 'services/fetchData' +import { authenticatedUserRoute } from 'utils/authenticatedUserRoute' import type { GetServerSideProps } from 'next' import type { Data } from 'types/Data' @@ -12,7 +13,10 @@ export default function DashPage({ initialData }: DashPageProps) { return } -export const getServerSideProps: GetServerSideProps = async () => { - const initialData = await fetchData() - return { props: { initialData } } -} +export const getServerSideProps: GetServerSideProps = authenticatedUserRoute( + async () => { + const initialData = await fetchData() + return { props: { initialData } } + }, + { roles: ['ADMIN', 'EDITOR'] } +) diff --git a/src/utils/authenticatedUserRoute.ts b/src/utils/authenticatedUserRoute.ts new file mode 100644 index 0000000..a419b93 --- /dev/null +++ b/src/utils/authenticatedUserRoute.ts @@ -0,0 +1,46 @@ +import { GetServerSideProps, GetServerSidePropsContext } from 'next' +import { parseCookies } from 'nookies' +import { decode } from 'jsonwebtoken' + +import { validateUserPermissions } from 'utils/validateUserPermissions' + +import type { Role } from '@prisma/client' + +type Options = { + roles: Role[] +} + +export function authenticatedUserRoute(fn: GetServerSideProps, options?: Options) { + return async (ctx: GetServerSidePropsContext) => { + const { accessToken } = parseCookies(ctx) + + if (!accessToken) { + return { + redirect: { + destination: '/auth', + permanent: false + } + } + } + + const { role: userRole } = decode(accessToken) as { role: Role } + + if (options) { + const userHasValidPermissions = validateUserPermissions({ + userRole, + roles: options.roles + }) + + if (!userHasValidPermissions) { + return { + redirect: { + destination: '/', + permanent: false + } + } + } + } + + return await fn(ctx) + } +} diff --git a/src/utils/guestUserRoute.ts b/src/utils/guestUserRoute.ts new file mode 100644 index 0000000..d8f5efe --- /dev/null +++ b/src/utils/guestUserRoute.ts @@ -0,0 +1,20 @@ +import { parseCookies } from 'nookies' + +import type { GetStaticProps, GetServerSidePropsContext } from 'next' + +export function guestUserRoute(fn: GetStaticProps) { + return async (ctx: GetServerSidePropsContext) => { + const { accessToken } = parseCookies(ctx) + + if (accessToken) { + return { + redirect: { + destination: '/dash', + permanent: false + } + } + } + + return await fn(ctx) + } +} diff --git a/src/utils/validateUserPermissions.ts b/src/utils/validateUserPermissions.ts new file mode 100644 index 0000000..e480867 --- /dev/null +++ b/src/utils/validateUserPermissions.ts @@ -0,0 +1,20 @@ +import type { Role } from '@prisma/client' + +type ValidateUserPermissionsParams = { + userRole: Role + roles?: Role[] +} + +export function validateUserPermissions({ userRole, roles }: ValidateUserPermissionsParams) { + if (roles && roles.length > 0) { + const hasPermission = roles.some(role => { + return userRole.includes(role) + }) + + if (!hasPermission) { + return false + } + } + + return true +}