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 { userId, email, name, password, requestedRole } = await req.json(); // Trying to update a different user than themselves // Only available to admins // todo add senior scientists being able to update their juniors if (userId && userId !== user.id) { if (user.role !== "ADMIN") { return NextResponse.json({ message: "Not authorised" }, { status: 401 }); } } // 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 }); } } // todo move to dedicated function // 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); } if (requestedRole && ["GUEST", "SCIENTIST", "ADMIN"].includes(requestedRole) && requestedRole !== user.role) { await prisma.request.create({ data: { requestType: requestedRole, requestingUserId: userId || user.id }, }); } const updatedUser = await prisma.user.update({ where: { id: userId || user.id }, data: { name: name || user.name, email: email || user.email, passwordHash, }, }); // Link non-account 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 }); } }