implemented change password functionality
This commit is contained in:
parent
59e4dc471f
commit
e774f41d37
|
@ -0,0 +1,79 @@
|
|||
// Copyright (C) 2022-present Daniel31x13 <daniel31x13@gmail.com>
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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 (
|
||||
<div className="sm:w-[33rem] w-72">
|
||||
<div className="max-w-sm mx-auto flex flex-col gap-3">
|
||||
<p className="text-xl text-sky-500 mb-2 text-center">Change Password</p>
|
||||
|
||||
<p className="text-sm font-bold text-sky-300">Old Password</p>
|
||||
|
||||
<input
|
||||
value={oldPassword}
|
||||
onChange={(e) => 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"
|
||||
/>
|
||||
<p className="text-sm font-bold text-sky-300">New Password</p>
|
||||
|
||||
<input
|
||||
value={newPassword1}
|
||||
onChange={(e) => 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"
|
||||
/>
|
||||
<p className="text-sm font-bold text-sky-300">Re-enter New Password</p>
|
||||
|
||||
<input
|
||||
value={newPassword2}
|
||||
onChange={(e) => 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"
|
||||
/>
|
||||
|
||||
<div
|
||||
className="mx-auto mt-2 text-white flex items-center gap-2 py-2 px-5 rounded-md select-none font-bold duration-100 bg-sky-500 hover:bg-sky-400 cursor-pointer"
|
||||
onClick={submit}
|
||||
>
|
||||
<FontAwesomeIcon icon={faPenToSquare} className="h-5" />
|
||||
Change Password
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -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<AccountSettings>({
|
||||
...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<AccountSettings>({
|
||||
...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) {
|
|||
<p className="text-sky-600">Profile Settings</p>
|
||||
|
||||
{user.email !== account.email || user.name !== account.name ? (
|
||||
<p className="text-gray-500 text-sm">
|
||||
<p className="text-gray-500 text-sm sm:w-1/2">
|
||||
Note: The page will be refreshed to apply the changes of "Email" or
|
||||
"Display Name".
|
||||
</p>
|
||||
|
@ -110,7 +129,7 @@ export default function UserSettings({ toggleSettingsModal }: Props) {
|
|||
<div>
|
||||
<p className="text-sm font-bold text-sky-300 mb-2">Password</p>
|
||||
|
||||
<div className="w-fit">
|
||||
<div className="w-fit" onClick={togglePasswordFormModal}>
|
||||
<div className="border border-sky-100 rounded-md bg-white px-2 py-1 text-center select-none cursor-pointer text-sky-900 duration-100 hover:border-sky-500">
|
||||
Change Password
|
||||
</div>
|
||||
|
@ -170,7 +189,7 @@ export default function UserSettings({ toggleSettingsModal }: Props) {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
{/* <hr /> TODO: Export functionality
|
||||
|
||||
<p className="text-sky-600">Data Settings</p>
|
||||
|
||||
|
@ -178,7 +197,7 @@ export default function UserSettings({ toggleSettingsModal }: Props) {
|
|||
<div className="border border-sky-100 rounded-md bg-white px-2 py-1 text-center select-none cursor-pointer text-sky-900 duration-100 hover:border-sky-500">
|
||||
Export Data
|
||||
</div>
|
||||
</div>
|
||||
</div> */}
|
||||
|
||||
<hr />
|
||||
|
||||
|
@ -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}
|
||||
>
|
||||
<FontAwesomeIcon icon={faPenToSquare} className="h-5" />
|
||||
Apply Settings
|
||||
</div>
|
||||
|
||||
{passwordFormModal ? (
|
||||
<Modal toggleModal={togglePasswordFormModal}>
|
||||
<ChangePassword
|
||||
user={user}
|
||||
togglePasswordFormModal={togglePasswordFormModal}
|
||||
setPasswordForm={setPasswordForm}
|
||||
/>
|
||||
</Modal>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -61,4 +61,6 @@ export type SearchSettings = {
|
|||
|
||||
export interface AccountSettings extends User {
|
||||
profilePic: string | null;
|
||||
oldPassword?: string;
|
||||
newPassword?: string;
|
||||
}
|
||||
|
|
Ŝarĝante…
Reference in New Issue