From e774f41d370e3953681ee2be6297d732c0dde7b5 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 22 May 2023 23:59:24 +0330 Subject: [PATCH] implemented change password functionality --- components/Modal/ChangePassword.tsx | 79 +++++++++++++++++++++++++ components/Modal/UserSettings.tsx | 54 +++++++++++++---- lib/api/controllers/users/updateUser.ts | 28 ++++++++- types/global.ts | 2 + 4 files changed, 149 insertions(+), 14 deletions(-) create mode 100644 components/Modal/ChangePassword.tsx diff --git a/components/Modal/ChangePassword.tsx b/components/Modal/ChangePassword.tsx new file mode 100644 index 0000000..2ccb4e7 --- /dev/null +++ b/components/Modal/ChangePassword.tsx @@ -0,0 +1,79 @@ +// Copyright (C) 2022-present Daniel31x13 +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3. +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +// You should have received a copy of the GNU General Public License along with this program. If not, see . + +import React, { useState } from "react"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { AccountSettings } from "@/types/global"; +import { faPenToSquare } from "@fortawesome/free-regular-svg-icons"; + +type Props = { + togglePasswordFormModal: Function; + user: AccountSettings; + setPasswordForm: Function; +}; + +export default function AddCollection({ + togglePasswordFormModal, + user, + setPasswordForm, +}: Props) { + const [oldPassword, setOldPassword] = useState(""); + const [newPassword1, setNewPassword1] = useState(""); + const [newPassword2, setNewPassword2] = useState(""); + + const submit = async () => { + if (oldPassword !== "" && newPassword1 !== "" && newPassword2 !== "") { + if (newPassword1 === newPassword2) { + setPasswordForm(oldPassword, newPassword1); + togglePasswordFormModal(); + } else { + console.log("Passwords do not match."); + } + } else { + console.log("Please fill out all the fields."); + } + }; + + return ( +
+
+

Change Password

+ +

Old Password

+ + setOldPassword(e.target.value)} + type="text" + className="w-full rounded-md p-3 mx-auto border-sky-100 border-solid border outline-none focus:border-sky-500 duration-100" + /> +

New Password

+ + setNewPassword1(e.target.value)} + type="text" + className="w-full rounded-md p-3 mx-auto border-sky-100 border-solid border outline-none focus:border-sky-500 duration-100" + /> +

Re-enter New Password

+ + setNewPassword2(e.target.value)} + type="text" + className="w-full rounded-md p-3 mx-auto border-sky-100 border-solid border outline-none focus:border-sky-500 duration-100" + /> + +
+ + Change Password +
+
+
+ ); +} diff --git a/components/Modal/UserSettings.tsx b/components/Modal/UserSettings.tsx index 4394bbf..06e7b47 100644 --- a/components/Modal/UserSettings.tsx +++ b/components/Modal/UserSettings.tsx @@ -12,6 +12,9 @@ import { AccountSettings } from "@/types/global"; import { useSession } from "next-auth/react"; import { resizeImage } from "@/lib/client/resizeImage"; import fileExists from "@/lib/client/fileExists"; +import Modal from "."; +import ChangePassword from "./ChangePassword"; +import { faPenToSquare } from "@fortawesome/free-regular-svg-icons"; type Props = { toggleSettingsModal: Function; @@ -21,6 +24,21 @@ export default function UserSettings({ toggleSettingsModal }: Props) { const { update } = useSession(); const { account, updateAccount } = useAccountStore(); + const [user, setUser] = useState({ + ...account, + profilePic: null, + }); + + const [passwordFormModal, setPasswordFormModal] = useState(false); + + const togglePasswordFormModal = () => { + setPasswordFormModal(!passwordFormModal); + }; + + const setPasswordForm = (oldPassword?: string, newPassword?: string) => { + setUser({ ...user, oldPassword, newPassword }); + }; + useEffect(() => { const determineProfilePicSource = async () => { const path = `/api/avatar/${account.id}`; @@ -31,11 +49,6 @@ export default function UserSettings({ toggleSettingsModal }: Props) { determineProfilePicSource(); }, []); - const [user, setUser] = useState({ - ...account, - profilePic: null, - }); - const handleImageUpload = async (e: any) => { const file: File = e.target.files[0]; @@ -45,8 +58,6 @@ export default function UserSettings({ toggleSettingsModal }: Props) { if (allowedExtensions.includes(fileExtension as string)) { const resizedFile = await resizeImage(file); - console.log(resizedFile.size); - if ( resizedFile.size < 1048576 // 1048576 Bytes == 1MB ) { @@ -66,7 +77,15 @@ export default function UserSettings({ toggleSettingsModal }: Props) { }; const submit = async () => { - await updateAccount(user); + await updateAccount({ + ...user, + profilePic: + user.profilePic === `/api/avatar/${account.id}` + ? null + : user.profilePic, + }); + + setPasswordForm(undefined, undefined); if (user.email !== account.email || user.name !== account.name) update({ email: user.email, name: user.name }); @@ -79,7 +98,7 @@ export default function UserSettings({ toggleSettingsModal }: Props) {

Profile Settings

{user.email !== account.email || user.name !== account.name ? ( -

+

Note: The page will be refreshed to apply the changes of "Email" or "Display Name".

@@ -110,7 +129,7 @@ export default function UserSettings({ toggleSettingsModal }: Props) {

Password

-
+
Change Password
@@ -170,7 +189,7 @@ export default function UserSettings({ toggleSettingsModal }: Props) {
-
+ {/*
TODO: Export functionality

Data Settings

@@ -178,7 +197,7 @@ export default function UserSettings({ toggleSettingsModal }: Props) {
Export Data
-
+ */}
@@ -210,8 +229,19 @@ export default function UserSettings({ toggleSettingsModal }: Props) { className="mx-auto mt-2 bg-sky-500 text-white flex items-center gap-2 py-2 px-5 rounded-md select-none font-bold cursor-pointer duration-100 hover:bg-sky-400" onClick={submit} > + Apply Settings + + {passwordFormModal ? ( + + + + ) : null} ); } diff --git a/lib/api/controllers/users/updateUser.ts b/lib/api/controllers/users/updateUser.ts index f719939..203cb70 100644 --- a/lib/api/controllers/users/updateUser.ts +++ b/lib/api/controllers/users/updateUser.ts @@ -7,10 +7,9 @@ import { prisma } from "@/lib/api/db"; import { AccountSettings } from "@/types/global"; import fs from "fs"; import path from "path"; +import bcrypt from "bcrypt"; export default async function (user: AccountSettings, userId: number) { - console.log(console.log(user.profilePic)); - const profilePic = user.profilePic; if (profilePic && profilePic !== "DELETE") { @@ -50,6 +49,31 @@ export default async function (user: AccountSettings, userId: number) { }, }); + if (user.newPassword && user.oldPassword) { + const saltRounds = 10; + + const requestOldHashedPassword = bcrypt.hashSync( + user.oldPassword, + saltRounds + ); + console.log(requestOldHashedPassword); + + if (bcrypt.compareSync(user.oldPassword, updatedUser.password)) { + const newHashedPassword = bcrypt.hashSync(user.newPassword, saltRounds); + + await prisma.user.update({ + where: { + id: userId, + }, + data: { + password: newHashedPassword, + }, + }); + } else { + return { response: "Passwords do not match.", status: 403 }; + } + } + const { password, ...unsensitiveInfo } = updatedUser; return { response: unsensitiveInfo, status: 200 }; diff --git a/types/global.ts b/types/global.ts index da3fff3..107cbab 100644 --- a/types/global.ts +++ b/types/global.ts @@ -61,4 +61,6 @@ export type SearchSettings = { export interface AccountSettings extends User { profilePic: string | null; + oldPassword?: string; + newPassword?: string; }