Prepare BizOne portal production wallet and UI
This commit is contained in:
@ -337,6 +337,9 @@ export class AuthService {
|
||||
name: true,
|
||||
email: true,
|
||||
status: true,
|
||||
role: {
|
||||
select: { name: true },
|
||||
},
|
||||
lastLoginAt: true,
|
||||
refreshTokenExpiresAt: true,
|
||||
twoFactorEnabled: true,
|
||||
@ -354,6 +357,7 @@ export class AuthService {
|
||||
name: user.name,
|
||||
email: user.email,
|
||||
status: user.status,
|
||||
roleName: user.role?.name || 'Unassigned',
|
||||
lastLoginAt: user.lastLoginAt,
|
||||
twoFactorEnabled: user.twoFactorEnabled,
|
||||
twoFactorConfirmedAt: user.twoFactorConfirmedAt,
|
||||
@ -368,6 +372,111 @@ export class AuthService {
|
||||
};
|
||||
}
|
||||
|
||||
async updateProfile(authUser: AuthenticatedUser, dto: { name?: string }, ipAddress?: string) {
|
||||
const current = await this.prisma.user.findUnique({
|
||||
where: { id: authUser.sub },
|
||||
select: { id: true, name: true, email: true },
|
||||
});
|
||||
|
||||
if (!current) {
|
||||
throw new UnauthorizedException('User not found');
|
||||
}
|
||||
|
||||
const name = dto.name !== undefined ? dto.name.trim() : current.name;
|
||||
|
||||
if (!name) {
|
||||
throw new HttpException('Name is required', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
const user = await this.prisma.user.update({
|
||||
where: { id: current.id },
|
||||
data: {
|
||||
name,
|
||||
},
|
||||
include: {
|
||||
role: {
|
||||
select: { name: true },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await this.prisma.auditLog.create({
|
||||
data: {
|
||||
actorUserId: user.id,
|
||||
actorName: user.name,
|
||||
actorEmail: user.email,
|
||||
actionType: 'Profile Updated',
|
||||
module: 'Account Profile',
|
||||
ipAddress: ipAddress || null,
|
||||
severity: 'default',
|
||||
details: `Updated profile for ${user.email}.`,
|
||||
},
|
||||
});
|
||||
|
||||
const response = {
|
||||
id: user.id,
|
||||
name: user.name,
|
||||
email: user.email,
|
||||
status: user.status,
|
||||
roleName: user.role?.name || 'Unassigned',
|
||||
lastLoginAt: user.lastLoginAt,
|
||||
twoFactorEnabled: user.twoFactorEnabled,
|
||||
twoFactorConfirmedAt: user.twoFactorConfirmedAt,
|
||||
};
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
async changePassword(authUser: AuthenticatedUser, currentPassword: string, newPassword: string, ipAddress?: string) {
|
||||
if (!hasMinimumPasswordLength(newPassword)) {
|
||||
throw new HttpException('New password must be at least 8 characters', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
const user = await this.prisma.user.findUnique({
|
||||
where: { id: authUser.sub },
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
email: true,
|
||||
passwordHash: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!user || !user.passwordHash) {
|
||||
throw new UnauthorizedException('User not found');
|
||||
}
|
||||
|
||||
const isPasswordValid = await comparePassword(currentPassword, user.passwordHash);
|
||||
if (!isPasswordValid) {
|
||||
throw new UnauthorizedException('Current password is incorrect');
|
||||
}
|
||||
|
||||
await this.prisma.user.update({
|
||||
where: { id: user.id },
|
||||
data: {
|
||||
passwordHash: await hashPassword(newPassword),
|
||||
sessionVersion: { increment: 1 },
|
||||
refreshTokenHash: null,
|
||||
refreshTokenExpiresAt: null,
|
||||
},
|
||||
});
|
||||
|
||||
await this.prisma.auditLog.create({
|
||||
data: {
|
||||
actorUserId: user.id,
|
||||
actorName: user.name,
|
||||
actorEmail: user.email,
|
||||
actionType: 'Password Changed',
|
||||
module: 'Account Profile',
|
||||
ipAddress: ipAddress || null,
|
||||
severity: 'warning',
|
||||
details: `Changed password for ${user.email}.`,
|
||||
},
|
||||
});
|
||||
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
async getTwoFactorStatus(userId: string) {
|
||||
const twoFactor = await this.getTwoFactorRecord(userId);
|
||||
if (!twoFactor) {
|
||||
|
||||
Reference in New Issue
Block a user