import { prisma } from '../config/database'; import { Prisma } from '@prisma/client'; import { config } from '../config'; import { logger } from '../config/logger'; import jwt from 'jsonwebtoken'; import bcrypt from 'bcrypt'; import { UserResponse, ProfileResponse, ProfileUpdate, ProfileUpdateResponse, ChangePasswordRequest, ChangePasswordResponse } from '../types/user.types'; import { UnauthorizedError, ValidationError, ForbiddenError } from '../middleware/errorHandler'; export class UserService { async getUserProfile(token: string): Promise { try { // Decode JWT token to get user ID const decoded = jwt.verify(token, config.jwt.secret) as { id: number; username: string; email: string; roleCode: string }; const user = await prisma.user.findUnique({ where: { id: decoded.id }, include: { profile: true, role: true } }); if (!user) throw new UnauthorizedError("User not found"); return { id: user.id, username: user.username, email: user.email, updated_at: user.updated_at, created_at: user.created_at, role: { code: user.role.code, name: user.role.name as { th: string; en: string } }, profile: user.profile ? { prefix: user.profile.prefix as { th?: string; en?: string } | undefined, first_name: user.profile.first_name, last_name: user.profile.last_name, avatar_url: user.profile.avatar_url, birth_date: user.profile.birth_date, phone: user.profile.phone } : undefined }; } catch (error) { if (error instanceof jwt.JsonWebTokenError) { logger.error('Invalid JWT token:', error); throw new UnauthorizedError('Invalid token'); } if (error instanceof jwt.TokenExpiredError) { logger.error('JWT token expired:', error); throw new UnauthorizedError('Token expired'); } logger.error('Error fetching user profile:', error); throw error; } }; /** * Change user password */ async changePassword(token: string, oldPassword: string, newPassword: string): Promise { try { // Decode JWT token to get user ID const decoded = jwt.verify(token, config.jwt.secret) as { id: number; username: string; email: string; roleCode: string }; const user = await prisma.user.findUnique({ where: { id: decoded.id } }); if (!user) throw new UnauthorizedError('User not found'); // Verify old password const isPasswordValid = await bcrypt.compare(oldPassword, user.password); if (!isPasswordValid) throw new UnauthorizedError('Invalid old password'); // Hash new password const encryptedPassword = await bcrypt.hash(newPassword, 10); // Update password await prisma.user.update({ where: { id: user.id }, data: { password: encryptedPassword } }); logger.info('Password changed successfully', { userId: user.id }); return { code: 200, message: 'Password changed successfully' }; } catch (error) { if (error instanceof jwt.JsonWebTokenError) { logger.error('Invalid JWT token:', error); throw new UnauthorizedError('Invalid token'); } if (error instanceof jwt.TokenExpiredError) { logger.error('JWT token expired:', error); throw new UnauthorizedError('Token expired'); } logger.error('Failed to change password', { error }); throw error; } } /** * Format user response */ private formatUserResponse(user: any): UserResponse { return { id: user.id, username: user.username, email: user.email, updated_at: user.updated_at, created_at: user.created_at, role: { code: user.role.code, name: user.role.name }, profile: user.profile ? { prefix: user.profile.prefix, first_name: user.profile.first_name, last_name: user.profile.last_name, avatar_url: user.profile.avatar_url, phone: user.profile.phone, birth_date: user.profile.birth_date } : undefined }; } }