diff --git a/src/app/api/login/route.ts b/src/app/api/login/route.ts index 38341b5..87c9ba3 100644 --- a/src/app/api/login/route.ts +++ b/src/app/api/login/route.ts @@ -5,17 +5,10 @@ import { NextResponse } from "next/server"; import { env } from "@utils/env"; import { prisma } from "@utils/prisma"; -import { findUserByEmail, readUserCsv, User } from "../functions/csvReadWrite"; - export async function POST(req: Request) { try { const { email, password } = await req.json(); // Parse incoming JSON data - const userData = await readUserCsv(); - console.log(userData); - console.log("Email:", email); // ! remove - console.log("Password:", password); // ! remove - let user = await prisma.user.findUnique({ where: { email, // use the email to uniquely identify the user diff --git a/src/app/api/signup/route.ts b/src/app/api/signup/route.ts index 52ca7a1..e0f6ad9 100644 --- a/src/app/api/signup/route.ts +++ b/src/app/api/signup/route.ts @@ -7,6 +7,8 @@ import { prisma } from "@utils/prisma"; import { passwordStrengthCheck } from "@utils/validation"; +// todo check email doesn't already exist + export async function POST(req: Request) { try { const { email, password, name } = await req.json(); diff --git a/src/app/api/update-user/route.ts b/src/app/api/update-user/route.ts new file mode 100644 index 0000000..8345426 --- /dev/null +++ b/src/app/api/update-user/route.ts @@ -0,0 +1,97 @@ +import { NextResponse } from "next/server"; +import bcryptjs from "bcryptjs"; + +import { env } from "@utils/env"; +import { prisma } from "@utils/prisma"; +import { apiAuthMiddleware } from "@utils/apiAuthMiddleware"; + +import { passwordStrengthCheck } from "@utils/validation"; + +export async function POST(req: Request) { + try { + const authResult = await apiAuthMiddleware(); + if ("user" in authResult === false) return authResult; + + const { user } = authResult; + const { email, name, password } = await req.json(); + + // Check if email is already in use by another user + if (email && email !== user.email) { + const foundUser = await prisma.user.findUnique({ + where: { email }, + }); + if (foundUser) { + return NextResponse.json({ message: "This email is already in use" }, { status: 409 }); + } + } + + // Validate password strength if provided + let passwordHash = user.passwordHash; + if (password) { + const passwordCheckResult = await passwordStrengthCheck(password); + if (passwordCheckResult === "short") { + return NextResponse.json({ message: "Password is shorter than 8 characters" }, { status: 400 }); + } else if (passwordCheckResult === "long") { + return NextResponse.json({ message: "Password is longer than 16 characters" }, { status: 400 }); + } else if (passwordCheckResult === "no lower") { + return NextResponse.json({ message: "Password must contain lowercase letters" }, { status: 400 }); + } else if (passwordCheckResult === "no upper") { + return NextResponse.json({ message: "Password must contain uppercase letters" }, { status: 400 }); + } else if (passwordCheckResult === "no digit") { + return NextResponse.json({ message: "Password must contain a number" }, { status: 400 }); + } else if (passwordCheckResult === "no special") { + return NextResponse.json({ message: "Password must contain a special character (!@#$%^&*)" }, { status: 400 }); + } else if (passwordCheckResult === "end of function") { + return NextResponse.json({ message: "Password check script failure" }, { status: 500 }); + } + passwordHash = await bcryptjs.hash(password, 10); + } + + // Update user in database + const updatedUser = await prisma.user.update({ + where: { id: user.id }, + data: { + name: name || user.name, + email: email || user.email, + passwordHash, + }, + }); + + // Link orders with matching email to the updated user + if (email && email !== user.email) { + await prisma.order.updateMany({ + where: { + email, + userId: null, + }, + data: { + userId: updatedUser.id, + }, + }); + } + + const fullUser = await prisma.user.findUnique({ + where: { id: updatedUser.id }, + include: { + earthquakes: true, + observatories: true, + artefacts: true, + purchasedOrders: true, + requests: true, + scientist: { + include: { + superior: true, + subordinates: true, + }, + }, + }, + }); + + const { passwordHash: _, ...userSansHash } = fullUser!; + + return NextResponse.json({ message: "User updated successfully", user: userSansHash }, { status: 200 }); + } catch (error) { + console.error("Error in update-user endpoint:", error); + return NextResponse.json({ message: "Internal Server Error" }, { status: 500 }); + } +} diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 077a1fe..0a30c39 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -34,8 +34,6 @@ const store = createStore({ }), }); -// todo fix user persisting on reload - function UserFetcher() { const setUser = useStoreActions((actions) => actions.setUser); const { data, error } = useSWR("/api/user", fetcher); diff --git a/src/components/AuthModal.tsx b/src/components/AuthModal.tsx index 5dcee33..fff3657 100644 --- a/src/components/AuthModal.tsx +++ b/src/components/AuthModal.tsx @@ -23,13 +23,13 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) { if (!isOpen) return null; // if is open is false, the model isnt shown - const handleOverlayClick = (e: MouseEvent) => { + function handleOverlayClick(e: MouseEvent) { if (modalRef.current && !modalRef.current.contains(e.target as Node)) { onClose(); } - }; + } - const handleSubmit = async (e: FormEvent) => { + async function handleSubmit(e: FormEvent) { e.preventDefault(); setIsFailed(false); @@ -74,7 +74,7 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) { setIsFailed(true); } } - }; + } return (