feat: Add token-based authorization to category deletion and enhance user registration with error handling and audit logging.

This commit is contained in:
JakkrapartXD 2026-02-12 17:55:45 +07:00
parent 45941fbe6c
commit af14610442
16 changed files with 1003 additions and 236 deletions

View file

@ -135,6 +135,17 @@ export class UserService {
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,
action: AuditAction.ERROR,
entityType: 'User',
entityId: decoded?.id || 0,
metadata: {
operation: 'change_password',
error: error instanceof Error ? error.message : String(error)
}
});
throw error;
}
}
@ -186,6 +197,17 @@ export class UserService {
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,
action: AuditAction.UPDATE,
entityType: 'UserProfile',
entityId: decoded?.id || 0,
metadata: {
operation: 'update_profile',
error: error instanceof Error ? error.message : String(error)
}
});
throw error;
}
}
@ -252,6 +274,18 @@ export class UserService {
});
}
// Audit log - UPLOAD_AVATAR
await auditService.logSync({
userId: decoded.id,
action: AuditAction.UPLOAD_FILE,
entityType: 'User',
entityId: decoded.id,
metadata: {
operation: 'upload_avatar',
filePath
}
});
// Generate presigned URL for response
const presignedUrl = await this.getAvatarPresignedUrl(filePath);
@ -273,6 +307,18 @@ export class UserService {
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,
action: AuditAction.UPLOAD_FILE,
entityType: 'UserProfile',
entityId: decoded?.id || 0,
metadata: {
operation: 'upload_avatar',
error: error instanceof Error ? error.message : String(error)
}
});
throw error;
}
}
@ -385,6 +431,17 @@ export class UserService {
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,
action: AuditAction.ERROR,
entityType: 'UserProfile',
entityId: decoded?.id || 0,
metadata: {
operation: 'send_verification_email',
error: error instanceof Error ? error.message : String(error)
}
});
throw error;
}
}
@ -415,6 +472,15 @@ export class UserService {
});
logger.info('Email verified successfully', { userId: user.id, email: user.email });
await auditService.logSync({
userId: user.id,
action: AuditAction.VERIFY_EMAIL,
entityType: 'UserProfile',
entityId: user.id,
metadata: {
operation: 'verify_email'
}
});
return {
code: 200,
message: 'Email verified successfully'
@ -423,6 +489,17 @@ export class UserService {
if (error instanceof jwt.JsonWebTokenError) throw new UnauthorizedError('Invalid verification token');
if (error instanceof jwt.TokenExpiredError) throw new UnauthorizedError('Verification link has expired');
logger.error('Failed to verify email', { error });
const decoded = jwt.decode(verifyToken) as { id: number } | null;
await auditService.logSync({
userId: decoded?.id || 0,
action: AuditAction.ERROR,
entityType: 'UserProfile',
entityId: decoded?.id || 0,
metadata: {
operation: 'verify_email',
error: error instanceof Error ? error.message : String(error)
}
});
throw error;
}
}