import { NextResponse } from "next/server"; import { prisma } from "@/lib/prisma"; import { Prisma } from "@prisma/client"; import bcrypt from "bcryptjs"; import { apiHandler, ApiError, requireUser } from "@/lib/api"; import { validateUsername, validatePassword, validateEmail } from "@/lib/validation"; import { getAuthUserId } from "@/lib/auth"; export const GET = apiHandler(async (req) => { // GET still allows querying by id param (for public profile viewing) // but sensitive fields are only shown to the owner const queryId = req.nextUrl.searchParams.get("id"); if (!queryId) return NextResponse.json(null); const user = await prisma.user.findUnique({ where: { id: queryId } }); if (!user) return NextResponse.json(null); const decisionCount = await prisma.decision.count({ where: { userId: queryId } }); // Check if the requester is the profile owner let isOwner = false; try { const authId = await getAuthUserId(req); isOwner = authId === queryId; } catch { // Not logged in — show public profile only } let preferences = {}; try { preferences = JSON.parse(user.preferences); } catch { /* fallback */ } return NextResponse.json({ id: user.id, username: user.username, avatar: user.avatar, email: isOwner ? user.email : undefined, preferences: isOwner ? preferences : undefined, createdAt: user.createdAt.toISOString(), decisionCount, }); }); export const PUT = apiHandler(async (req) => { const userId = await getAuthUserId(req); const body = await req.json(); const existing = await requireUser(userId); const updateData: Record = {}; if (body.username !== undefined) { const trimmed = validateUsername(body.username); if (trimmed !== existing.username) { const taken = await prisma.user.findUnique({ where: { username: trimmed } }); if (taken) throw new ApiError("用户名已被占用", 409); } updateData.username = trimmed; } if (body.newPassword !== undefined) { if (!body.currentPassword) throw new ApiError("请输入当前密码"); const valid = await bcrypt.compare(body.currentPassword, existing.passwordHash); if (!valid) throw new ApiError("当前密码错误", 403); validatePassword(body.newPassword, "新密码"); updateData.passwordHash = await bcrypt.hash(body.newPassword, 10); } if (body.avatar !== undefined) { updateData.avatar = body.avatar; } if (body.email !== undefined) { if (body.email) validateEmail(body.email); updateData.email = body.email || null; } if (body.preferences !== undefined) { updateData.preferences = JSON.stringify(body.preferences); } try { const user = await prisma.user.update({ where: { id: userId }, data: updateData, }); let prefs = {}; try { prefs = JSON.parse(user.preferences); } catch { /* fallback */ } return NextResponse.json({ id: user.id, username: user.username, avatar: user.avatar, email: user.email, preferences: prefs, }); } catch (e) { if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === "P2002") { throw new ApiError("用户名已被占用", 409); } throw e; } });