From 9b097f22783054b10b8052ab553948a3ea932cfd Mon Sep 17 00:00:00 2001 From: Bartek Paczesny Date: Fri, 22 Dec 2023 22:48:52 +0100 Subject: [PATCH] Add file upload and delete hooks to profilePictures bucket, implement deleteUser function, and add password update route --- app/api/edgestore/[...edgestore]/route.ts | 9 +- app/api/user/password/route.ts | 63 +++++++++ app/api/user/route.ts | 21 +++ app/panel/settings/account.tsx | 115 ++++++--------- app/panel/settings/functions.ts | 164 ++++++++++++++++++++++ utils/student.ts | 27 +++- utils/user.ts | 16 +++ 7 files changed, 339 insertions(+), 76 deletions(-) create mode 100644 app/api/user/password/route.ts create mode 100644 app/panel/settings/functions.ts diff --git a/app/api/edgestore/[...edgestore]/route.ts b/app/api/edgestore/[...edgestore]/route.ts index 4078f04..9edd4e2 100644 --- a/app/api/edgestore/[...edgestore]/route.ts +++ b/app/api/edgestore/[...edgestore]/route.ts @@ -13,7 +13,14 @@ const es = initEdgeStore.create(); * This is the main router for the Edge Store buckets. */ const edgeStoreRouter = es.router({ - profilePictures: es.fileBucket(), + profilePictures: es + .fileBucket() + .beforeUpload(({ ctx, input, fileInfo }) => { + return true; // allow upload + }) + .beforeDelete(({ ctx, fileInfo }) => { + return true; // allow delete + }), }); const handler = createEdgeStoreNextHandler({ diff --git a/app/api/user/password/route.ts b/app/api/user/password/route.ts new file mode 100644 index 0000000..34e0561 --- /dev/null +++ b/app/api/user/password/route.ts @@ -0,0 +1,63 @@ +import { NextResponse } from "next/server"; +import bcrypt from "bcrypt"; +import { prisma } from "@/prisma/client"; + +type Props = { + currentPassword: string; + newPassword: string; + userId: number; +}; + +export async function PATCH(req: Request) { + const body = await req.json(); + + const { currentPassword, newPassword, userId } = body as Props; + + try { + const user = await prisma.user.findUnique({ + where: { id: userId }, + }); + + if (user === null) { + return new NextResponse( + JSON.stringify({ + error: "Nie znaleziono użytkownika.", + }), + { status: 404 } + ); + } + + const passwordMatch = await bcrypt.compare( + currentPassword, + user.password + ); + + if (!passwordMatch) { + return new NextResponse( + JSON.stringify({ + error: "Niepoprawne hasło.", + }), + { status: 400 } + ); + } + + const salt = await bcrypt.genSalt(10); + const hash = await bcrypt.hash(newPassword, salt); + + await prisma.user.update({ + where: { id: userId }, + data: { + password: hash, + }, + }); + + return new NextResponse(null, { status: 200 }); + } catch (e: any) { + return new NextResponse( + JSON.stringify({ + error: e, + }), + { status: 500 } + ); + } +} diff --git a/app/api/user/route.ts b/app/api/user/route.ts index 38f29f2..5cfb807 100644 --- a/app/api/user/route.ts +++ b/app/api/user/route.ts @@ -1,4 +1,5 @@ import { prisma } from "@/prisma/client"; +import { deleteUser } from "@/utils/user"; import { NextResponse } from "next/server"; type Props = { @@ -26,3 +27,23 @@ export async function PATCH(req: Request) { return new NextResponse(null, { status: 200 }); } + +export async function DELETE(req: Request) { + const body = await req.json(); + + const { userId } = body as Props; + + try { + await deleteUser(userId); + + return new NextResponse(null, { status: 200 }); + } catch (e) { + console.log(e); + return new NextResponse( + JSON.stringify({ + error: e, + }), + { status: 500 } + ); + } +} diff --git a/app/panel/settings/account.tsx b/app/panel/settings/account.tsx index f68a082..28ba47e 100644 --- a/app/panel/settings/account.tsx +++ b/app/panel/settings/account.tsx @@ -1,6 +1,5 @@ "use client"; -import type { Session } from "next-auth"; import { signOut, useSession } from "next-auth/react"; import Image from "next/image"; import { useEffect, useState } from "react"; @@ -8,74 +7,11 @@ import { toast } from "react-toastify"; import { useEdgeStore } from "@/lib/edgestore"; import clsx from "clsx"; import { Loader2 } from "lucide-react"; - -async function handleUserChange( - e: FormData, - session: Session, - setLoading: (loading: boolean) => void, - edgestore: any, - avatarFile: File | undefined -) { - setLoading(true); - let avatarUrl = ""; - - if (session.user.avatar === "" && avatarFile !== undefined) { - // upload the new avatar - const res = await edgestore.profilePictures.upload({ - file: avatarFile, - }); - - avatarUrl = res.url; - } else if (session.user.avatar !== "" && avatarFile !== undefined) { - const res = await edgestore.profilePictures.upload({ - file: avatarFile, - options: { - replaceTargetUrl: session.user.avatar, - }, - }); - - avatarUrl = res.url; - } - - if ( - session?.user?.firstName !== e.get("first-name") || - session?.user?.lastName !== e.get("last-name") || - session?.user?.email !== e.get("email") || - avatarUrl !== "" - ) { - const res = await fetch("/api/user", { - method: "PATCH", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - firstName: e.get("first-name"), - lastName: e.get("last-name"), - email: e.get("email"), - avatar: avatarUrl, - userId: session.user.id, - }), - }); - - if (res.status === 200) { - setLoading(false); - toast.success( - "Zaktualizowano informacje. Za chwilę zostaniesz wylogowany." - ); - await new Promise((resolve) => setTimeout(resolve, 2500)); - signOut({ - callbackUrl: "/", - redirect: true, - }); - } else { - setLoading(false); - toast.error("Wystąpił błąd podczas aktualizacji informacji."); - } - } else { - setLoading(false); - toast.info("Nie zmieniono żadnych informacji."); - } -} +import { + handleDeleteAccount, + handlePasswordChange, + handleUserChange, +} from "./functions"; type Props = {}; @@ -111,7 +47,9 @@ function Account({}: Props) { session!, setLoading, edgestore, - avatarFile + avatarFile, + toast, + signOut ); }} className="md:col-span-2" @@ -253,7 +191,17 @@ function Account({}: Props) {

-
+ { + await handlePasswordChange( + session!, + event, + setLoading, + toast + ); + }} + className="md:col-span-2" + >
@@ -333,7 +289,18 @@ function Account({}: Props) {

-
+ + await handleDeleteAccount( + Number(session?.user.id!), + signOut, + toast, + edgestore, + userAvatar! + ) + } + className="flex items-start md:col-span-2" + >