feat: Add token-based authorization to category deletion and enhance user registration with error handling and audit logging.
This commit is contained in:
parent
45941fbe6c
commit
af14610442
16 changed files with 1003 additions and 236 deletions
|
|
@ -83,167 +83,201 @@ export class AuthService {
|
|||
* User registration
|
||||
*/
|
||||
async register(data: RegisterRequest): Promise<RegisterResponse> {
|
||||
const { username, email, password, first_name, last_name, prefix, phone } = data;
|
||||
try {
|
||||
const { username, email, password, first_name, last_name, prefix, phone } = data;
|
||||
|
||||
// Check if username already exists
|
||||
const existingUsername = await prisma.user.findUnique({
|
||||
where: { username }
|
||||
});
|
||||
// Check if username already exists
|
||||
const existingUsername = await prisma.user.findUnique({
|
||||
where: { username }
|
||||
});
|
||||
|
||||
if (existingUsername) {
|
||||
throw new ValidationError('Username already exists');
|
||||
}
|
||||
|
||||
// Check if email already exists
|
||||
const existingEmail = await prisma.user.findUnique({
|
||||
where: { email }
|
||||
});
|
||||
|
||||
if (existingEmail) {
|
||||
throw new ValidationError('Email already exists');
|
||||
}
|
||||
|
||||
// Check if phone number already exists in user profiles
|
||||
const existingPhone = await prisma.userProfile.findFirst({
|
||||
where: { phone }
|
||||
});
|
||||
|
||||
if (existingPhone) {
|
||||
throw new ValidationError('Phone number already exists');
|
||||
}
|
||||
|
||||
// Get STUDENT role
|
||||
const studentRole = await prisma.role.findUnique({
|
||||
where: { code: 'STUDENT' }
|
||||
});
|
||||
|
||||
if (!studentRole) {
|
||||
logger.error('STUDENT role not found in database');
|
||||
throw new Error('System configuration error');
|
||||
}
|
||||
|
||||
// Hash password
|
||||
const hashedPassword = await bcrypt.hash(password, 10);
|
||||
|
||||
// Create user with profile
|
||||
const user = await prisma.user.create({
|
||||
data: {
|
||||
username,
|
||||
email,
|
||||
password: hashedPassword,
|
||||
role_id: studentRole.id,
|
||||
profile: {
|
||||
create: {
|
||||
prefix: prefix ? prefix as Prisma.InputJsonValue : Prisma.JsonNull,
|
||||
first_name,
|
||||
last_name,
|
||||
phone
|
||||
}
|
||||
}
|
||||
},
|
||||
include: {
|
||||
role: true,
|
||||
profile: true
|
||||
if (existingUsername) {
|
||||
throw new ValidationError('Username already exists');
|
||||
}
|
||||
});
|
||||
|
||||
logger.info('New user registered', { userId: user.id, username: user.username });
|
||||
// Check if email already exists
|
||||
const existingEmail = await prisma.user.findUnique({
|
||||
where: { email }
|
||||
});
|
||||
|
||||
// Audit log - REGISTER (Student)
|
||||
auditService.log({
|
||||
userId: user.id,
|
||||
action: AuditAction.CREATE,
|
||||
entityType: 'User',
|
||||
entityId: user.id,
|
||||
newValue: { username: user.username, email: user.email, role: 'STUDENT' }
|
||||
});
|
||||
if (existingEmail) {
|
||||
throw new ValidationError('Email already exists');
|
||||
}
|
||||
|
||||
return {
|
||||
user: this.formatUserResponseSync(user),
|
||||
message: 'Registration successful'
|
||||
};
|
||||
// Check if phone number already exists in user profiles
|
||||
const existingPhone = await prisma.userProfile.findFirst({
|
||||
where: { phone }
|
||||
});
|
||||
|
||||
if (existingPhone) {
|
||||
throw new ValidationError('Phone number already exists');
|
||||
}
|
||||
|
||||
// Get STUDENT role
|
||||
const studentRole = await prisma.role.findUnique({
|
||||
where: { code: 'STUDENT' }
|
||||
});
|
||||
|
||||
if (!studentRole) {
|
||||
logger.error('STUDENT role not found in database');
|
||||
throw new Error('System configuration error');
|
||||
}
|
||||
|
||||
// Hash password
|
||||
const hashedPassword = await bcrypt.hash(password, 10);
|
||||
|
||||
// Create user with profile
|
||||
const user = await prisma.user.create({
|
||||
data: {
|
||||
username,
|
||||
email,
|
||||
password: hashedPassword,
|
||||
role_id: studentRole.id,
|
||||
profile: {
|
||||
create: {
|
||||
prefix: prefix ? prefix as Prisma.InputJsonValue : Prisma.JsonNull,
|
||||
first_name,
|
||||
last_name,
|
||||
phone
|
||||
}
|
||||
}
|
||||
},
|
||||
include: {
|
||||
role: true,
|
||||
profile: true
|
||||
}
|
||||
});
|
||||
|
||||
logger.info('New user registered', { userId: user.id, username: user.username });
|
||||
|
||||
// Audit log - REGISTER (Student)
|
||||
auditService.log({
|
||||
userId: user.id,
|
||||
action: AuditAction.CREATE,
|
||||
entityType: 'User',
|
||||
entityId: user.id,
|
||||
newValue: { username: user.username, email: user.email, role: 'STUDENT' }
|
||||
});
|
||||
|
||||
return {
|
||||
user: this.formatUserResponseSync(user),
|
||||
message: 'Registration successful'
|
||||
};
|
||||
} catch (error) {
|
||||
logger.error('Failed to register user', { error });
|
||||
await auditService.logSync({
|
||||
userId: 0,
|
||||
action: AuditAction.ERROR,
|
||||
entityType: 'User',
|
||||
entityId: 0,
|
||||
metadata: {
|
||||
operation: 'register_user',
|
||||
email: data.email,
|
||||
username: data.username,
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
}
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async registerInstructor(data: RegisterRequest): Promise<RegisterResponse> {
|
||||
const { username, email, password, first_name, last_name, prefix, phone } = data;
|
||||
try {
|
||||
const { username, email, password, first_name, last_name, prefix, phone } = data;
|
||||
|
||||
// Check if username already exists
|
||||
const existingUsername = await prisma.user.findUnique({
|
||||
where: { username }
|
||||
});
|
||||
// Check if username already exists
|
||||
const existingUsername = await prisma.user.findUnique({
|
||||
where: { username }
|
||||
});
|
||||
|
||||
if (existingUsername) {
|
||||
throw new ValidationError('Username already exists');
|
||||
}
|
||||
|
||||
// Check if email already exists
|
||||
const existingEmail = await prisma.user.findUnique({
|
||||
where: { email }
|
||||
});
|
||||
|
||||
if (existingEmail) {
|
||||
throw new ValidationError('Email already exists');
|
||||
}
|
||||
|
||||
// Check if phone number already exists in user profiles
|
||||
const existingPhone = await prisma.userProfile.findFirst({
|
||||
where: { phone }
|
||||
});
|
||||
|
||||
if (existingPhone) {
|
||||
throw new ValidationError('Phone number already exists');
|
||||
}
|
||||
|
||||
// Get INSTRUCTOR role
|
||||
const instructorRole = await prisma.role.findUnique({
|
||||
where: { code: 'INSTRUCTOR' }
|
||||
});
|
||||
|
||||
if (!instructorRole) {
|
||||
logger.error('INSTRUCTOR role not found in database');
|
||||
throw new Error('System configuration error');
|
||||
}
|
||||
|
||||
// Hash password
|
||||
const hashedPassword = await bcrypt.hash(password, 10);
|
||||
|
||||
// Create user with profile
|
||||
const user = await prisma.user.create({
|
||||
data: {
|
||||
username,
|
||||
email,
|
||||
password: hashedPassword,
|
||||
role_id: instructorRole.id,
|
||||
profile: {
|
||||
create: {
|
||||
prefix: prefix ? prefix as Prisma.InputJsonValue : Prisma.JsonNull,
|
||||
first_name,
|
||||
last_name,
|
||||
phone
|
||||
}
|
||||
}
|
||||
},
|
||||
include: {
|
||||
role: true,
|
||||
profile: true
|
||||
if (existingUsername) {
|
||||
throw new ValidationError('Username already exists');
|
||||
}
|
||||
});
|
||||
|
||||
logger.info('New user registered', { userId: user.id, username: user.username });
|
||||
// Check if email already exists
|
||||
const existingEmail = await prisma.user.findUnique({
|
||||
where: { email }
|
||||
});
|
||||
|
||||
// Audit log - REGISTER (Instructor)
|
||||
auditService.log({
|
||||
userId: user.id,
|
||||
action: AuditAction.CREATE,
|
||||
entityType: 'User',
|
||||
entityId: user.id,
|
||||
newValue: { username: user.username, email: user.email, role: 'INSTRUCTOR' }
|
||||
});
|
||||
if (existingEmail) {
|
||||
throw new ValidationError('Email already exists');
|
||||
}
|
||||
|
||||
return {
|
||||
user: this.formatUserResponseSync(user),
|
||||
message: 'Registration successful'
|
||||
};
|
||||
// Check if phone number already exists in user profiles
|
||||
const existingPhone = await prisma.userProfile.findFirst({
|
||||
where: { phone }
|
||||
});
|
||||
|
||||
if (existingPhone) {
|
||||
throw new ValidationError('Phone number already exists');
|
||||
}
|
||||
|
||||
// Get INSTRUCTOR role
|
||||
const instructorRole = await prisma.role.findUnique({
|
||||
where: { code: 'INSTRUCTOR' }
|
||||
});
|
||||
|
||||
if (!instructorRole) {
|
||||
logger.error('INSTRUCTOR role not found in database');
|
||||
throw new Error('System configuration error');
|
||||
}
|
||||
|
||||
// Hash password
|
||||
const hashedPassword = await bcrypt.hash(password, 10);
|
||||
|
||||
// Create user with profile
|
||||
const user = await prisma.user.create({
|
||||
data: {
|
||||
username,
|
||||
email,
|
||||
password: hashedPassword,
|
||||
role_id: instructorRole.id,
|
||||
profile: {
|
||||
create: {
|
||||
prefix: prefix ? prefix as Prisma.InputJsonValue : Prisma.JsonNull,
|
||||
first_name,
|
||||
last_name,
|
||||
phone
|
||||
}
|
||||
}
|
||||
},
|
||||
include: {
|
||||
role: true,
|
||||
profile: true
|
||||
}
|
||||
});
|
||||
|
||||
logger.info('New user registered', { userId: user.id, username: user.username });
|
||||
|
||||
// Audit log - REGISTER (Instructor)
|
||||
auditService.log({
|
||||
userId: user.id,
|
||||
action: AuditAction.CREATE,
|
||||
entityType: 'User',
|
||||
entityId: user.id,
|
||||
newValue: { username: user.username, email: user.email, role: 'INSTRUCTOR' }
|
||||
});
|
||||
|
||||
return {
|
||||
user: this.formatUserResponseSync(user),
|
||||
message: 'Registration successful'
|
||||
};
|
||||
} catch (error) {
|
||||
logger.error('Failed to register instructor', { error });
|
||||
await auditService.logSync({
|
||||
userId: 0,
|
||||
action: AuditAction.ERROR,
|
||||
entityType: 'User',
|
||||
entityId: 0,
|
||||
metadata: {
|
||||
operation: 'register_instructor',
|
||||
email: data.email,
|
||||
username: data.username,
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
}
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue