refactor: update user identification to pass userId directly to services instead of JWT tokens.
Some checks failed
Build and Deploy Backend / Build Backend Docker Image (push) Successful in 48s
Build and Deploy Backend / Deploy E-learning Backend to Dev Server (push) Successful in 9s
Build and Deploy Backend / Notify Deployment Status (push) Successful in 2s
Build and Deploy Frontend Learner / Build Frontend Learner Docker Image (push) Failing after 33s
Build and Deploy Frontend Learner / Deploy E-learning Frontend Learner to Dev Server (push) Has been skipped
Build and Deploy Frontend Learner / Notify Deployment Status (push) Failing after 1s

This commit is contained in:
JakkrapartXD 2026-03-04 17:19:58 +07:00
parent b6c1aebe30
commit 522a0eec8a
28 changed files with 558 additions and 952 deletions

View file

@ -24,15 +24,10 @@ import { auditService } from './audit.service';
import { AuditAction } from '@prisma/client';
export class UserService {
async getUserProfile(token: string): Promise<UserResponse> {
async getUserProfile(userId: number): Promise<UserResponse> {
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
},
where: { id: userId },
include: {
profile: true,
role: true
@ -68,14 +63,6 @@ export class UserService {
} : 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;
}
@ -84,12 +71,9 @@ export class UserService {
/**
* Change user password
*/
async changePassword(token: string, oldPassword: string, newPassword: string): Promise<ChangePasswordResponse> {
async changePassword(userId: number, oldPassword: string, newPassword: string): Promise<ChangePasswordResponse> {
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 } });
const user = await prisma.user.findUnique({ where: { id: userId } });
if (!user) throw new UnauthorizedError('User not found');
// Check if account is deactivated
@ -127,21 +111,12 @@ export class UserService {
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 });
const decoded = jwt.decode(token) as { id: number } | null;
await auditService.logSync({
userId: decoded?.id || 0,
userId,
action: AuditAction.ERROR,
entityType: 'User',
entityId: decoded?.id || 0,
entityId: userId,
metadata: {
operation: 'change_password',
error: error instanceof Error ? error.message : String(error)
@ -154,12 +129,9 @@ export class UserService {
/**
* Update user profile
*/
async updateProfile(token: string, profile: ProfileUpdate): Promise<ProfileUpdateResponse> {
async updateProfile(userId: number, profile: ProfileUpdate): Promise<ProfileUpdateResponse> {
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 } });
const user = await prisma.user.findUnique({ where: { id: userId } });
if (!user) throw new UnauthorizedError('User not found');
// Check if account is deactivated
@ -189,21 +161,12 @@ export class UserService {
}
};
} 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 update profile', { error });
const decoded = jwt.decode(token) as { id: number } | null;
await auditService.logSync({
userId: decoded?.id || 0,
userId,
action: AuditAction.UPDATE,
entityType: 'UserProfile',
entityId: decoded?.id || 0,
entityId: userId,
metadata: {
operation: 'update_profile',
error: error instanceof Error ? error.message : String(error)
@ -213,9 +176,8 @@ export class UserService {
}
}
async getRoles(token: string): Promise<rolesResponse> {
async getRoles(): Promise<rolesResponse> {
try {
jwt.verify(token, config.jwt.secret);
const roles = await prisma.role.findMany({
select: {
id: true,
@ -224,14 +186,6 @@ export class UserService {
});
return { roles };
} catch (error) {
if (error instanceof jwt.TokenExpiredError) {
logger.error('JWT token expired:', error);
throw new UnauthorizedError('Token expired');
}
if (error instanceof jwt.JsonWebTokenError) {
logger.error('Invalid JWT token:', error);
throw new UnauthorizedError('Invalid token');
}
logger.error('Failed to get roles', { error });
throw error;
}
@ -240,13 +194,11 @@ export class UserService {
/**
* Upload avatar picture to MinIO
*/
async uploadAvatarPicture(token: string, file: Express.Multer.File): Promise<updateAvatarResponse> {
async uploadAvatarPicture(userId: number, file: Express.Multer.File): Promise<updateAvatarResponse> {
try {
const decoded = jwt.verify(token, config.jwt.secret) as { id: number };
// Check if user exists
const user = await prisma.user.findUnique({
where: { id: decoded.id },
where: { id: userId },
include: { profile: true }
});
@ -265,7 +217,7 @@ export class UserService {
const fileName = file.originalname || 'avatar';
const extension = fileName.split('.').pop() || 'jpg';
const safeFilename = `${timestamp}-${uniqueId}.${extension}`;
const filePath = `avatars/${decoded.id}/${safeFilename}`;
const filePath = `avatars/${userId}/${safeFilename}`;
// Delete old avatar if exists
if (user.profile?.avatar_url) {
@ -285,13 +237,13 @@ export class UserService {
// Update or create profile - store only file path
if (user.profile) {
await prisma.userProfile.update({
where: { user_id: decoded.id },
where: { user_id: userId },
data: { avatar_url: filePath }
});
} else {
await prisma.userProfile.create({
data: {
user_id: decoded.id,
user_id: userId,
avatar_url: filePath,
first_name: '',
last_name: ''
@ -301,10 +253,10 @@ export class UserService {
// Audit log - UPLOAD_AVATAR
await auditService.logSync({
userId: decoded.id,
userId,
action: AuditAction.UPLOAD_FILE,
entityType: 'User',
entityId: decoded.id,
entityId: userId,
metadata: {
operation: 'upload_avatar',
filePath
@ -318,26 +270,17 @@ export class UserService {
code: 200,
message: 'Avatar uploaded successfully',
data: {
id: decoded.id,
id: userId,
avatar_url: presignedUrl
}
};
} 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 upload avatar', { error });
const decoded = jwt.decode(token) as { id: number } | null;
await auditService.logSync({
userId: decoded?.id || 0,
userId,
action: AuditAction.UPLOAD_FILE,
entityType: 'UserProfile',
entityId: decoded?.id || 0,
entityId: userId,
metadata: {
operation: 'upload_avatar',
error: error instanceof Error ? error.message : String(error)
@ -390,12 +333,10 @@ export class UserService {
/**
* Send verification email to user
*/
async sendVerifyEmail(token: string): Promise<SendVerifyEmailResponse> {
async sendVerifyEmail(userId: number): Promise<SendVerifyEmailResponse> {
try {
const decoded = jwt.verify(token, config.jwt.secret) as { id: number; email: string; roleCode: string };
const user = await prisma.user.findUnique({
where: { id: decoded.id },
where: { id: userId },
include: { role: true }
});
@ -453,15 +394,12 @@ export class UserService {
message: 'Verification email sent successfully'
};
} catch (error) {
if (error instanceof jwt.JsonWebTokenError) throw new UnauthorizedError('Invalid token');
if (error instanceof jwt.TokenExpiredError) throw new UnauthorizedError('Token expired');
logger.error('Failed to send verification email', { error });
const decoded = jwt.decode(token) as { id: number } | null;
await auditService.logSync({
userId: decoded?.id || 0,
userId,
action: AuditAction.ERROR,
entityType: 'UserProfile',
entityId: decoded?.id || 0,
entityId: userId,
metadata: {
operation: 'send_verification_email',
error: error instanceof Error ? error.message : String(error)