From 400fa3d480842ed099cfd0ae6b29e74d2d82d446 Mon Sep 17 00:00:00 2001 From: Thomas Camlong Date: Fri, 10 May 2024 13:36:46 +0200 Subject: [PATCH 01/11] propose forcing lowercase usernames --- packages/api/src/router/user.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/api/src/router/user.ts b/packages/api/src/router/user.ts index 364a3ff82..e57fd2fa3 100644 --- a/packages/api/src/router/user.ts +++ b/packages/api/src/router/user.ts @@ -127,10 +127,12 @@ const createUser = async ( const salt = await createSalt(); const hashedPassword = await hashPassword(input.password, salt); + const username = input.username.toLowerCase(); + const userId = createId(); await db.insert(schema.users).values({ id: userId, - name: input.username, + name: username, password: hashedPassword, salt, }); From 781247de5141f4b0532801194ac904763db7da39 Mon Sep 17 00:00:00 2001 From: Thomas Camlong Date: Fri, 10 May 2024 13:39:21 +0200 Subject: [PATCH 02/11] feat: add username validation to prevent duplicate --- packages/api/src/router/user.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/api/src/router/user.ts b/packages/api/src/router/user.ts index e57fd2fa3..f548a7f15 100644 --- a/packages/api/src/router/user.ts +++ b/packages/api/src/router/user.ts @@ -32,6 +32,16 @@ export const userRouter = createTRPCRouter({ create: publicProcedure .input(validation.user.create) .mutation(async ({ ctx, input }) => { + const user = await ctx.db.query.users.findFirst({ + where: eq(users.name, input.username), + }); + + if (user !== null) { + throw new TRPCError({ + code: "FORBIDDEN", + message: "User already exists", + }); + } await createUser(ctx.db, input); }), getAll: publicProcedure.query(async ({ ctx }) => { From 245c2f483c36e197da4e8ff2a2fe1d58e2916786 Mon Sep 17 00:00:00 2001 From: Thomas Camlong Date: Fri, 10 May 2024 14:16:24 +0200 Subject: [PATCH 03/11] feat: Add ErrorDisplay component --- apps/nextjs/src/components/utils.tsx | 33 ++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 apps/nextjs/src/components/utils.tsx diff --git a/apps/nextjs/src/components/utils.tsx b/apps/nextjs/src/components/utils.tsx new file mode 100644 index 000000000..1262ec877 --- /dev/null +++ b/apps/nextjs/src/components/utils.tsx @@ -0,0 +1,33 @@ +import type { AlertProps } from "@mantine/core"; +import { Alert } from "@mantine/core"; +import { IconAlertTriangle } from "@tabler/icons-react"; + +interface ErrorDisplayProps extends AlertProps { + title?: string; + hidden?: boolean; + message?: string; + icon?: React.ReactNode; +} + +export function ErrorDisplay({ + title = "There was an error", + message, + icon, + hidden = false, + ...alertProps +}: ErrorDisplayProps) { + if (hidden) { + return null; + } + return ( + } + {...alertProps} + > + {message} + + ); +} From c2026d0962e6038e89641fae398db7e3581d2e8b Mon Sep 17 00:00:00 2001 From: Thomas Camlong Date: Fri, 10 May 2024 14:16:45 +0200 Subject: [PATCH 04/11] refactor: Update UserCreateStepperComponent To use ErrorDisplay component --- .../users/create/_components/create-user-stepper.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/nextjs/src/app/[locale]/manage/users/create/_components/create-user-stepper.tsx b/apps/nextjs/src/app/[locale]/manage/users/create/_components/create-user-stepper.tsx index c1d903110..7ae062cce 100644 --- a/apps/nextjs/src/app/[locale]/manage/users/create/_components/create-user-stepper.tsx +++ b/apps/nextjs/src/app/[locale]/manage/users/create/_components/create-user-stepper.tsx @@ -1,6 +1,5 @@ "use client"; -import { useCallback, useMemo, useState } from "react"; import { Avatar, Card, @@ -12,12 +11,14 @@ import { Title, } from "@mantine/core"; import { IconUserCheck } from "@tabler/icons-react"; +import { useCallback, useMemo, useState } from "react"; import { clientApi } from "@homarr/api/client"; import { useForm, zodResolver } from "@homarr/form"; import { useScopedI18n } from "@homarr/translation/client"; import { validation, z } from "@homarr/validation"; +import { ErrorDisplay } from "~/components/utils"; import { StepperNavigationComponent } from "./stepper-navigation.component"; export const UserCreateStepperComponent = () => { @@ -38,7 +39,8 @@ export const UserCreateStepperComponent = () => { const hasNext = active < stepperMax; const hasPrevious = active > 0; - const { mutateAsync, isPending } = clientApi.user.create.useMutation(); + const { mutateAsync, isPending, isError, error } = + clientApi.user.create.useMutation(); const generalForm = useForm({ initialValues: { @@ -107,6 +109,7 @@ export const UserCreateStepperComponent = () => { return ( <> {t("title")} +