From 240d92aeae97a4cd167f7973cabcf1fe14d69016 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 23 May 2023 07:38:16 +0330 Subject: [PATCH] added the ability for the users to hide there profile --- components/Modal/AddCollection.tsx | 11 +--- components/Modal/ChangePassword.tsx | 5 +- components/Modal/UserSettings.tsx | 56 ++++++++++++++++--- lib/api/controllers/users/getUsers.ts | 16 +++++- lib/api/controllers/users/updateUser.ts | 2 +- lib/client/getPublicUserDataByEmail.ts | 2 + pages/api/avatar/[id].ts | 30 ++++++++-- pages/api/routes/users/index.ts | 2 +- .../migration.sql | 2 +- prisma/schema.prisma | 2 +- 10 files changed, 94 insertions(+), 34 deletions(-) rename prisma/migrations/{20230522122002_init => 20230523040650_init}/migration.sql (98%) diff --git a/components/Modal/AddCollection.tsx b/components/Modal/AddCollection.tsx index 26c5d3b..dba8e7a 100644 --- a/components/Modal/AddCollection.tsx +++ b/components/Modal/AddCollection.tsx @@ -10,6 +10,7 @@ import useCollectionStore from "@/store/collections"; import { NewCollection } from "@/types/global"; import { useSession } from "next-auth/react"; import RequiredBadge from "../RequiredBadge"; +import getPublicUserDataByEmail from "@/lib/client/getPublicUserDataByEmail"; type Props = { toggleCollectionModal: Function; @@ -36,14 +37,6 @@ export default function AddCollection({ toggleCollectionModal }: Props) { if (response) toggleCollectionModal(); }; - const getUserByEmail = async (email: string) => { - const response = await fetch(`/api/routes/users?email=${email}`); - - const data = await response.json(); - - return data.response; - }; - return (

New Collection

@@ -107,7 +100,7 @@ export default function AddCollection({ toggleCollectionModal }: Props) { memberEmail.trim() !== ownerEmail ) { // Lookup, get data/err, list ... - const user = await getUserByEmail(memberEmail.trim()); + const user = await getPublicUserDataByEmail(memberEmail.trim()); if (user.email) { const newMember = { diff --git a/components/Modal/ChangePassword.tsx b/components/Modal/ChangePassword.tsx index 2ccb4e7..110315e 100644 --- a/components/Modal/ChangePassword.tsx +++ b/components/Modal/ChangePassword.tsx @@ -4,9 +4,7 @@ // 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; @@ -70,8 +68,7 @@ export default function AddCollection({ 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} > - - Change Password + Save
diff --git a/components/Modal/UserSettings.tsx b/components/Modal/UserSettings.tsx index 06e7b47..45fe518 100644 --- a/components/Modal/UserSettings.tsx +++ b/components/Modal/UserSettings.tsx @@ -29,6 +29,9 @@ export default function UserSettings({ toggleSettingsModal }: Props) { profilePic: null, }); + const [whitelistedUsersTextbox, setWhiteListedUsersTextbox] = useState( + user.whitelistedUsers.join(", ") + ); const [passwordFormModal, setPasswordFormModal] = useState(false); const togglePasswordFormModal = () => { @@ -49,6 +52,21 @@ export default function UserSettings({ toggleSettingsModal }: Props) { determineProfilePicSource(); }, []); + useEffect(() => { + setUser({ + ...user, + whitelistedUsers: stringToArray(whitelistedUsersTextbox), + }); + }, [whitelistedUsersTextbox]); + + const stringToArray = (str: string) => { + const stringWithoutSpaces = str.replace(/\s+/g, ""); + + const wordsArray = stringWithoutSpaces.split(","); + + return wordsArray; + }; + const handleImageUpload = async (e: any) => { const file: File = e.target.files[0]; @@ -77,6 +95,8 @@ export default function UserSettings({ toggleSettingsModal }: Props) { }; const submit = async () => { + console.log(user); + await updateAccount({ ...user, profilePic: @@ -134,6 +154,14 @@ export default function UserSettings({ toggleSettingsModal }: Props) { Change Password + + {user.newPassword && user.oldPassword ? ( +

+ Password modified. Please click{" "} + "Apply Settings" to + apply the changes.. +

+ ) : null} @@ -204,23 +232,33 @@ export default function UserSettings({ toggleSettingsModal }: Props) {

Privacy Settings

- setUser({ ...user, collectionProtection: !user.collectionProtection }) - } + onClick={() => setUser({ ...user, isPrivate: !user.isPrivate })} /> - {user.collectionProtection ? ( +

+ This will limit who can find and add you to other Collections. +

+ + {user.isPrivate ? (
+

+ Whitelisted Users +

- Please enter the email addresses of the users who are allowed to add - you to additional collections in the box below, separated by spaces. + Please provide the Email addresses of the users you wish to grant + visibility to your profile. Separate the addresses with a comma. + Users not included will be unable to view your profile.

) : null} diff --git a/lib/api/controllers/users/getUsers.ts b/lib/api/controllers/users/getUsers.ts index 4237b18..582fdfd 100644 --- a/lib/api/controllers/users/getUsers.ts +++ b/lib/api/controllers/users/getUsers.ts @@ -5,14 +5,26 @@ import { prisma } from "@/lib/api/db"; -export default async function (lookupEmail: string, isSelf: boolean) { +export default async function ( + lookupEmail: string, + isSelf: boolean, + userEmail: string +) { const user = await prisma.user.findUnique({ where: { email: lookupEmail, }, }); - if (!user) return { response: "User not found." || null, status: 404 }; + if (!user) return { response: "User not found.", status: 404 }; + + if ( + !isSelf && + user?.isPrivate && + !user.whitelistedUsers.includes(userEmail) + ) { + return { response: "This profile is private.", status: 401 }; + } const { password, ...unsensitiveInfo } = user; diff --git a/lib/api/controllers/users/updateUser.ts b/lib/api/controllers/users/updateUser.ts index 203cb70..2630867 100644 --- a/lib/api/controllers/users/updateUser.ts +++ b/lib/api/controllers/users/updateUser.ts @@ -44,7 +44,7 @@ export default async function (user: AccountSettings, userId: number) { data: { name: user.name, email: user.email, - collectionProtection: user.collectionProtection, + isPrivate: user.isPrivate, whitelistedUsers: user.whitelistedUsers, }, }); diff --git a/lib/client/getPublicUserDataByEmail.ts b/lib/client/getPublicUserDataByEmail.ts index a91fa07..d386918 100644 --- a/lib/client/getPublicUserDataByEmail.ts +++ b/lib/client/getPublicUserDataByEmail.ts @@ -8,5 +8,7 @@ export default async function (email: string) { const data = await response.json(); + console.log(data); + return data.response; } diff --git a/pages/api/avatar/[id].ts b/pages/api/avatar/[id].ts index ae85c21..cd3aa87 100644 --- a/pages/api/avatar/[id].ts +++ b/pages/api/avatar/[id].ts @@ -6,23 +6,41 @@ import type { NextApiRequest, NextApiResponse } from "next"; import { getServerSession } from "next-auth/next"; import { authOptions } from "pages/api/auth/[...nextauth]"; +import { prisma } from "@/lib/api/db"; import path from "path"; import fs from "fs"; export default async function (req: NextApiRequest, res: NextApiResponse) { - if (!req.query.id) - return res.status(401).json({ response: "Invalid parameters." }); - const session = await getServerSession(req, res, authOptions); - if (!session?.user?.email) + const userId = session?.user.id; + const userEmail = session?.user.email; + const queryId = Number(req.query.id); + + if (!queryId) + return res.status(401).json({ response: "Invalid parameters." }); + + if (!userId || !userEmail) return res.status(401).json({ response: "You must be logged in." }); - // TODO: If profile is private, hide it to other users... + if (userId !== queryId) { + const targetUser = await prisma.user.findUnique({ + where: { + id: queryId, + }, + }); + + if ( + targetUser?.isPrivate && + !targetUser.whitelistedUsers.includes(userEmail) + ) { + return res.status(401).json({ response: "This profile is private." }); + } + } const filePath = path.join( process.cwd(), - `data/uploads/avatar/${req.query.id}.jpg` + `data/uploads/avatar/${queryId}.jpg` ); console.log(filePath); diff --git a/pages/api/routes/users/index.ts b/pages/api/routes/users/index.ts index 75ca851..4af3e19 100644 --- a/pages/api/routes/users/index.ts +++ b/pages/api/routes/users/index.ts @@ -20,7 +20,7 @@ export default async function (req: NextApiRequest, res: NextApiResponse) { const isSelf = session.user.email === lookupEmail ? true : false; if (req.method === "GET") { - const users = await getUsers(lookupEmail, isSelf); + const users = await getUsers(lookupEmail, isSelf, session.user.email); return res.status(users.status).json({ response: users.response }); } else if (req.method === "PUT" && !req.body.password) { const updated = await updateUser(req.body, session.user.id); diff --git a/prisma/migrations/20230522122002_init/migration.sql b/prisma/migrations/20230523040650_init/migration.sql similarity index 98% rename from prisma/migrations/20230522122002_init/migration.sql rename to prisma/migrations/20230523040650_init/migration.sql index 770d6a9..351f017 100644 --- a/prisma/migrations/20230522122002_init/migration.sql +++ b/prisma/migrations/20230523040650_init/migration.sql @@ -4,7 +4,7 @@ CREATE TABLE "User" ( "name" TEXT NOT NULL, "email" TEXT NOT NULL, "password" TEXT NOT NULL, - "collectionProtection" BOOLEAN NOT NULL DEFAULT false, + "isPrivate" BOOLEAN NOT NULL DEFAULT false, "whitelistedUsers" TEXT[] DEFAULT ARRAY[]::TEXT[], "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 5930434..f80505e 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -20,7 +20,7 @@ model User { collections Collection[] tags Tag[] collectionsJoined UsersAndCollections[] - collectionProtection Boolean @default(false) + isPrivate Boolean @default(false) whitelistedUsers String[] @default([]) createdAt DateTime @default(now()) }