const { prisma } = require('../utils/db'); const { hashPassword, comparePassword, generateToken, generateVerificationToken, generateVerificationCode } = require('../utils/auth'); const { sendVerificationEmail } = require('../utils/email'); const { sanitizeForEmail } = require('../utils/sanitize'); /** * Update user profile * PATCH /api/users/me */ async function updateProfile(req, res, next) { try { const userId = req.user.id; const { firstName, lastName, email, wsdcId, youtubeUrl, instagramUrl, facebookUrl, tiktokUrl } = req.body; // Build update data const updateData = {}; if (firstName !== undefined) updateData.firstName = firstName; if (lastName !== undefined) updateData.lastName = lastName; if (wsdcId !== undefined) updateData.wsdcId = wsdcId || null; // Allow empty string to clear WSDC ID if (youtubeUrl !== undefined) updateData.youtubeUrl = youtubeUrl || null; if (instagramUrl !== undefined) updateData.instagramUrl = instagramUrl || null; if (facebookUrl !== undefined) updateData.facebookUrl = facebookUrl || null; if (tiktokUrl !== undefined) updateData.tiktokUrl = tiktokUrl || null; // Check if email is being changed const currentUser = await prisma.user.findUnique({ where: { id: userId }, select: { email: true }, }); let emailChanged = false; if (email && email !== currentUser.email) { // Check if new email is already taken by another user const existingUser = await prisma.user.findUnique({ where: { email }, }); if (existingUser && existingUser.id !== userId) { return res.status(400).json({ success: false, error: 'Email is already registered to another account', }); } // Email is being changed - require re-verification updateData.email = email; updateData.emailVerified = false; updateData.verificationToken = generateVerificationToken(); updateData.verificationCode = generateVerificationCode(); updateData.verificationTokenExpiry = new Date(Date.now() + 24 * 60 * 60 * 1000); // 24 hours emailChanged = true; } // Update user const updatedUser = await prisma.user.update({ where: { id: userId }, data: updateData, }); // If email changed, send verification email if (emailChanged) { try { await sendVerificationEmail( updatedUser.email, sanitizeForEmail(updatedUser.firstName || updatedUser.username), updatedUser.verificationToken, updatedUser.verificationCode ); } catch (emailError) { console.error('Failed to send verification email:', emailError); // Continue anyway - user is updated but email might not have been sent } } // Generate new JWT token (in case emailVerified changed) const token = generateToken({ userId: updatedUser.id }); // Remove sensitive data const { passwordHash, verificationToken, verificationCode, verificationTokenExpiry, resetToken, resetTokenExpiry, ...userWithoutPassword } = updatedUser; res.json({ success: true, message: emailChanged ? 'Profile updated. Please verify your new email address.' : 'Profile updated successfully', data: { user: userWithoutPassword, token, emailChanged, }, }); } catch (error) { next(error); } } /** * Change password * PATCH /api/users/me/password */ async function changePassword(req, res, next) { try { const userId = req.user.id; const { currentPassword, newPassword } = req.body; if (!currentPassword || !newPassword) { return res.status(400).json({ success: false, error: 'Current password and new password are required', }); } // Get user with password const user = await prisma.user.findUnique({ where: { id: userId }, }); // Verify current password const isPasswordValid = await comparePassword(currentPassword, user.passwordHash); if (!isPasswordValid) { return res.status(401).json({ success: false, error: 'Current password is incorrect', }); } // Hash new password const hashedPassword = await hashPassword(newPassword); // Update password await prisma.user.update({ where: { id: userId }, data: { passwordHash: hashedPassword }, }); res.json({ success: true, message: 'Password changed successfully', }); } catch (error) { next(error); } } module.exports = { updateProfile, changePassword, };