From 306151db651466cb6cc1ac82d9f8085a563e9380 Mon Sep 17 00:00:00 2001 From: Manuel Date: Sat, 5 Aug 2023 12:35:06 +0200 Subject: [PATCH 1/3] =?UTF-8?q?=E2=9C=A8=20Add=20link=20to=20manage=20page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/layout/header/AvatarMenu.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/layout/header/AvatarMenu.tsx b/src/components/layout/header/AvatarMenu.tsx index 75b7e175b..0ee06a83b 100644 --- a/src/components/layout/header/AvatarMenu.tsx +++ b/src/components/layout/header/AvatarMenu.tsx @@ -2,12 +2,13 @@ import { Avatar, Badge, Menu, UnstyledButton, useMantineTheme } from '@mantine/c import { useDisclosure } from '@mantine/hooks'; import { IconDashboard, + IconHomeShare, IconInfoCircle, IconLogin, IconLogout, IconMoonStars, IconSun, - IconUserCog, + IconUserCog } from '@tabler/icons-react'; import { useQuery } from '@tanstack/react-query'; import { User } from 'next-auth'; @@ -51,6 +52,9 @@ export const AvatarMenu = () => { }> Default Dashboard + }> + Manage + )} From 5b1b36eecc9765067df7e737e65485aef603580c Mon Sep 17 00:00:00 2001 From: Manuel Date: Sat, 5 Aug 2023 15:09:48 +0200 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=9A=B8=20Remember=20current=20values?= =?UTF-8?q?=20as=20default=20in=20create=20user=20stepper?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Manage/User/Create/create-account-step.tsx | 8 +++++--- .../Manage/User/Create/security-step.tsx | 4 +++- src/components/layout/header/AvatarMenu.tsx | 2 +- src/pages/manage/users/create.tsx | 15 +++++++++++++-- 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/components/Manage/User/Create/create-account-step.tsx b/src/components/Manage/User/Create/create-account-step.tsx index b06531559..d2fda30c6 100644 --- a/src/components/Manage/User/Create/create-account-step.tsx +++ b/src/components/Manage/User/Create/create-account-step.tsx @@ -5,13 +5,15 @@ import { z } from 'zod'; interface CreateAccountStepProps { nextStep: ({ eMail, username }: { username: string; eMail: string }) => void; + defaultUsername: string; + defaultEmail: string; } -export const CreateAccountStep = ({ nextStep }: CreateAccountStepProps) => { +export const CreateAccountStep = ({ defaultEmail, defaultUsername, nextStep }: CreateAccountStepProps) => { const form = useForm({ initialValues: { - username: '', - eMail: '', + username: defaultUsername, + eMail: defaultEmail, }, validateInputOnBlur: true, validateInputOnChange: true, diff --git a/src/components/Manage/User/Create/security-step.tsx b/src/components/Manage/User/Create/security-step.tsx index afccbacd6..17143faa5 100644 --- a/src/components/Manage/User/Create/security-step.tsx +++ b/src/components/Manage/User/Create/security-step.tsx @@ -43,17 +43,19 @@ function getStrength(password: string) { } interface CreateAccountSecurityStepProps { + defaultPassword: string; nextStep: ({ password }: { password: string }) => void; prevStep: () => void; } export const CreateAccountSecurityStep = ({ + defaultPassword, nextStep, prevStep, }: CreateAccountSecurityStepProps) => { const form = useForm({ initialValues: { - password: '', + password: defaultPassword, }, validateInputOnBlur: true, validateInputOnChange: true, diff --git a/src/components/layout/header/AvatarMenu.tsx b/src/components/layout/header/AvatarMenu.tsx index 0ee06a83b..c3bbcb47d 100644 --- a/src/components/layout/header/AvatarMenu.tsx +++ b/src/components/layout/header/AvatarMenu.tsx @@ -8,7 +8,7 @@ import { IconLogout, IconMoonStars, IconSun, - IconUserCog + IconUserCog, } from '@tabler/icons-react'; import { useQuery } from '@tanstack/react-query'; import { User } from 'next-auth'; diff --git a/src/pages/manage/users/create.tsx b/src/pages/manage/users/create.tsx index 92962fa7b..d8d1631a2 100644 --- a/src/pages/manage/users/create.tsx +++ b/src/pages/manage/users/create.tsx @@ -73,6 +73,8 @@ const CreateNewUserPage = () => { description="Create account" > { form.setFieldValue('account', value); nextStep(); @@ -87,6 +89,7 @@ const CreateNewUserPage = () => { description="Password" > { form.setFieldValue('security', value); nextStep(); @@ -160,7 +163,15 @@ const CreateNewUserPage = () => { - + + - + From cf057505ec57861e034b874fd457a5344e42a2c4 Mon Sep 17 00:00:00 2001 From: Manuel Date: Sat, 5 Aug 2023 15:30:59 +0200 Subject: [PATCH 3/3] =?UTF-8?q?=E2=9C=A8=20Protect=20routes=20and=20proced?= =?UTF-8?q?ures?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../layout/Templates/ManageLayout.tsx | 72 +++++++++++-------- src/pages/manage/boards/index.tsx | 25 +++++++ src/pages/manage/index.tsx | 25 +++++++ src/pages/manage/settings/index.tsx | 25 +++++++ src/pages/manage/users/create.tsx | 25 +++++++ src/pages/manage/users/index.tsx | 25 +++++++ src/pages/manage/users/invites.tsx | 25 +++++++ src/server/api/routers/board.ts | 4 +- src/server/api/routers/password.ts | 10 +-- src/server/api/routers/user.ts | 16 +++-- 10 files changed, 209 insertions(+), 43 deletions(-) diff --git a/src/components/layout/Templates/ManageLayout.tsx b/src/components/layout/Templates/ManageLayout.tsx index 2dfc71fff..18a7dc86b 100644 --- a/src/components/layout/Templates/ManageLayout.tsx +++ b/src/components/layout/Templates/ManageLayout.tsx @@ -26,6 +26,7 @@ import { IconUser, IconUsers, } from '@tabler/icons-react'; +import { useSession } from 'next-auth/react'; import Image from 'next/image'; import Link from 'next/link'; import { ReactNode } from 'react'; @@ -47,6 +48,9 @@ export const ManageLayout = ({ children }: ManageLayoutProps) => { const [burgerMenuOpen, { toggle: toggleBurgerMenu, close: closeBurgerMenu }] = useDisclosure(false); + const data = useSession(); + const isAdmin = data.data?.user.isAdmin ?? false; + const navigationLinks = ( <> { component={Link} href="/manage/boards" /> - - - - } - > - } - label="Manage" - component={Link} - href="/manage/users" - /> - } - label="Invites" - component={Link} - href="/manage/users/invites" - /> - - - - - } - component={Link} - href="/manage/settings" - /> + + {isAdmin && ( + <> + + + + } + > + } + label="Manage" + component={Link} + href="/manage/users" + /> + } + label="Invites" + component={Link} + href="/manage/users/invites" + /> + + + + + } + component={Link} + href="/manage/settings" + /> + + )} + { @@ -196,4 +199,26 @@ const BoardsPage = () => { ); }; +export const getServerSideProps: GetServerSideProps = async (ctx) => { + const session = await getServerAuthSession(ctx); + + if (!session?.user) { + return { + notFound: true, + }; + } + + const translations = await getServerSideTranslations( + ['common'], + ctx.locale, + undefined, + undefined + ); + return { + props: { + ...translations, + }, + }; +}; + export default BoardsPage; diff --git a/src/pages/manage/index.tsx b/src/pages/manage/index.tsx index c56ef8f8a..7eb27801d 100644 --- a/src/pages/manage/index.tsx +++ b/src/pages/manage/index.tsx @@ -10,12 +10,15 @@ import { createStyles, } from '@mantine/core'; import { IconArrowRight } from '@tabler/icons-react'; +import { GetServerSideProps } from 'next'; import { useSession } from 'next-auth/react'; import Head from 'next/head'; import Image from 'next/image'; import Link from 'next/link'; import { ManageLayout } from '~/components/layout/Templates/ManageLayout'; import { useScreenLargerThan } from '~/hooks/useScreenLargerThan'; +import { getServerAuthSession } from '~/server/auth'; +import { getServerSideTranslations } from '~/tools/server/getServerSideTranslations'; const ManagementPage = () => { const { classes } = useStyles(); @@ -102,6 +105,28 @@ const ManagementPage = () => { ); }; +export const getServerSideProps: GetServerSideProps = async (ctx) => { + const session = await getServerAuthSession(ctx); + + if (!session?.user) { + return { + notFound: true, + }; + } + + const translations = await getServerSideTranslations( + ['common'], + ctx.locale, + undefined, + undefined + ); + return { + props: { + ...translations, + }, + }; +}; + export default ManagementPage; const useStyles = createStyles((theme) => ({ diff --git a/src/pages/manage/settings/index.tsx b/src/pages/manage/settings/index.tsx index 57bc4a9a7..04607cf30 100644 --- a/src/pages/manage/settings/index.tsx +++ b/src/pages/manage/settings/index.tsx @@ -1,6 +1,9 @@ import { Text, Title } from '@mantine/core'; +import { GetServerSideProps } from 'next'; import Head from 'next/head'; import { ManageLayout } from '~/components/layout/Templates/ManageLayout'; +import { getServerAuthSession } from '~/server/auth'; +import { getServerSideTranslations } from '~/tools/server/getServerSideTranslations'; const SettingsPage = () => { return ( @@ -15,4 +18,26 @@ const SettingsPage = () => { ); }; +export const getServerSideProps: GetServerSideProps = async (ctx) => { + const session = await getServerAuthSession(ctx); + + if (!session?.user.isAdmin) { + return { + notFound: true, + }; + } + + const translations = await getServerSideTranslations( + ['common'], + ctx.locale, + undefined, + undefined + ); + return { + props: { + ...translations, + }, + }; +}; + export default SettingsPage; diff --git a/src/pages/manage/users/create.tsx b/src/pages/manage/users/create.tsx index d8d1631a2..93c5de48a 100644 --- a/src/pages/manage/users/create.tsx +++ b/src/pages/manage/users/create.tsx @@ -10,6 +10,7 @@ import { IconUser, IconUserPlus, } from '@tabler/icons-react'; +import { GetServerSideProps } from 'next'; import Head from 'next/head'; import Link from 'next/link'; import { useState } from 'react'; @@ -23,6 +24,8 @@ import { createAccountSecurityStepValidationSchema, } from '~/components/Manage/User/Create/security-step'; import { ManageLayout } from '~/components/layout/Templates/ManageLayout'; +import { getServerAuthSession } from '~/server/auth'; +import { getServerSideTranslations } from '~/tools/server/getServerSideTranslations'; import { api } from '~/utils/api'; const CreateNewUserPage = () => { @@ -221,4 +224,26 @@ const CreateNewUserPage = () => { ); }; +export const getServerSideProps: GetServerSideProps = async (ctx) => { + const session = await getServerAuthSession(ctx); + + if (!session?.user.isAdmin) { + return { + notFound: true, + }; + } + + const translations = await getServerSideTranslations( + ['common'], + ctx.locale, + undefined, + undefined + ); + return { + props: { + ...translations, + }, + }; +}; + export default CreateNewUserPage; diff --git a/src/pages/manage/users/index.tsx b/src/pages/manage/users/index.tsx index 00f9d985b..73715b966 100644 --- a/src/pages/manage/users/index.tsx +++ b/src/pages/manage/users/index.tsx @@ -14,10 +14,13 @@ import { import { useDebouncedValue } from '@mantine/hooks'; import { openContextModal } from '@mantine/modals'; import { IconPlus, IconTrash } from '@tabler/icons-react'; +import { GetServerSideProps } from 'next'; import Head from 'next/head'; import Link from 'next/link'; import { useState } from 'react'; import { ManageLayout } from '~/components/layout/Templates/ManageLayout'; +import { getServerAuthSession } from '~/server/auth'; +import { getServerSideTranslations } from '~/tools/server/getServerSideTranslations'; import { api } from '~/utils/api'; const ManageUsersPage = () => { @@ -134,4 +137,26 @@ const ManageUsersPage = () => { ); }; +export const getServerSideProps: GetServerSideProps = async (ctx) => { + const session = await getServerAuthSession(ctx); + + if (!session?.user.isAdmin) { + return { + notFound: true, + }; + } + + const translations = await getServerSideTranslations( + ['common'], + ctx.locale, + undefined, + undefined + ); + return { + props: { + ...translations, + }, + }; +}; + export default ManageUsersPage; diff --git a/src/pages/manage/users/invites.tsx b/src/pages/manage/users/invites.tsx index 673fc045b..4f9974f3e 100644 --- a/src/pages/manage/users/invites.tsx +++ b/src/pages/manage/users/invites.tsx @@ -12,10 +12,13 @@ import { import { modals } from '@mantine/modals'; import { IconPlus, IconTrash } from '@tabler/icons-react'; import dayjs from 'dayjs'; +import { GetServerSideProps } from 'next'; import Head from 'next/head'; import { useRouter } from 'next/router'; import { useState } from 'react'; import { ManageLayout } from '~/components/layout/Templates/ManageLayout'; +import { getServerAuthSession } from '~/server/auth'; +import { getServerSideTranslations } from '~/tools/server/getServerSideTranslations'; import { api } from '~/utils/api'; const ManageUserInvitesPage = () => { @@ -151,4 +154,26 @@ const useStyles = createStyles(() => ({ }, })); +export const getServerSideProps: GetServerSideProps = async (ctx) => { + const session = await getServerAuthSession(ctx); + + if (!session?.user.isAdmin) { + return { + notFound: true, + }; + } + + const translations = await getServerSideTranslations( + ['common'], + ctx.locale, + undefined, + undefined + ); + return { + props: { + ...translations, + }, + }; +}; + export default ManageUserInvitesPage; diff --git a/src/server/api/routers/board.ts b/src/server/api/routers/board.ts index 99ecb58fa..0a4d25af6 100644 --- a/src/server/api/routers/board.ts +++ b/src/server/api/routers/board.ts @@ -1,11 +1,11 @@ import fs from 'fs'; -import { createTRPCRouter, publicProcedure } from '../trpc'; +import { createTRPCRouter, protectedProcedure, publicProcedure } from '../trpc'; import { getFrontendConfig } from '~/tools/config/getFrontendConfig'; export const boardRouter = createTRPCRouter({ - all: publicProcedure.query(async ({ ctx }) => { + all: protectedProcedure.query(async ({ ctx }) => { const files = fs.readdirSync('./data/configs').filter((file) => file.endsWith('.json')); const userSettings = await ctx.prisma.userSettings.findUniqueOrThrow({ diff --git a/src/server/api/routers/password.ts b/src/server/api/routers/password.ts index e96a78a84..1b9e708a3 100644 --- a/src/server/api/routers/password.ts +++ b/src/server/api/routers/password.ts @@ -1,9 +1,9 @@ import { generate } from 'generate-password'; -import { createTRPCRouter, publicProcedure } from "../trpc"; +import { adminProcedure, createTRPCRouter } from '../trpc'; export const passwordRouter = createTRPCRouter({ - generate: publicProcedure.mutation(() => { + generate: adminProcedure.mutation(() => { return generate({ strict: true, numbers: true, @@ -11,7 +11,7 @@ export const passwordRouter = createTRPCRouter({ uppercase: true, symbols: true, excludeSimilarCharacters: true, - length: 16 - }) + length: 16, + }); }), -}); \ No newline at end of file +}); diff --git a/src/server/api/routers/user.ts b/src/server/api/routers/user.ts index ad04942c4..5a8bfe416 100644 --- a/src/server/api/routers/user.ts +++ b/src/server/api/routers/user.ts @@ -11,7 +11,13 @@ import { } from '~/validations/user'; import { COOKIE_COLOR_SCHEME_KEY, COOKIE_LOCALE_KEY } from '../../../../data/constants'; -import { TRPCContext, createTRPCRouter, protectedProcedure, publicProcedure } from '../trpc'; +import { + TRPCContext, + adminProcedure, + createTRPCRouter, + protectedProcedure, + publicProcedure, +} from '../trpc'; export const userRouter = createTRPCRouter({ createAdminAccount: publicProcedure.input(signUpFormSchema).mutation(async ({ ctx, input }) => { @@ -182,7 +188,7 @@ export const userRouter = createTRPCRouter({ }); }), - makeDefaultDashboard: publicProcedure + makeDefaultDashboard: protectedProcedure .input(z.object({ board: z.string() })) .mutation(async ({ ctx, input }) => { await ctx.prisma.userSettings.update({ @@ -195,7 +201,7 @@ export const userRouter = createTRPCRouter({ }); }), - all: publicProcedure + all: adminProcedure .input( z.object({ limit: z.number().min(1).max(100).default(10), @@ -236,11 +242,11 @@ export const userRouter = createTRPCRouter({ countPages: Math.ceil(countUsers / limit), }; }), - create: publicProcedure.input(createNewUserSchema).mutation(async ({ ctx, input }) => { + create: adminProcedure.input(createNewUserSchema).mutation(async ({ ctx, input }) => { await createUserInNotExist(ctx, input); }), - deleteUser: publicProcedure + deleteUser: adminProcedure .input( z.object({ userId: z.string(),