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) {
-