Prepare BizOne portal production wallet and UI

This commit is contained in:
2026-05-22 13:13:10 +07:00
parent 36be8607e0
commit 5144207c42
124 changed files with 11034 additions and 7720 deletions

View File

@ -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) {