refactor(iam): remove remaining prisma.user refs from passenger side align RegisterDto to IAM body

This commit is contained in:
Abubeker Yasin
2026-06-09 11:53:46 +03:00
parent 1d35453338
commit 5ae6d9600a
6 changed files with 296 additions and 374 deletions

View File

@@ -1,43 +1,43 @@
import { IsEmail, IsString, MinLength, IsOptional } from 'class-validator';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { IsEmail, IsString, MinLength, ValidateNested } from 'class-validator';
import { Type } from 'class-transformer';
import { ApiProperty } from '@nestjs/swagger';
export class NameDto {
@ApiProperty({ example: 'ቀለሙ ቀጸላ' })
@IsString()
am: string;
export class RegisterDto {
@ApiProperty({ example: 'Kelemu Ketsela' })
@IsString()
fullName: string;
en: string;
}
export class RegisterDto {
@ApiProperty({ example: 'kelemu@email.com' })
@IsEmail()
email: string;
@ApiProperty({ example: 'kelemu.ketsela' })
@IsString()
username: string;
@ApiProperty({ example: '+251912345678' })
@IsString()
phone: string;
phoneNumber: string;
@ApiProperty({ type: NameDto })
@ValidateNested()
@Type(() => NameDto)
name: NameDto;
@ApiProperty({ example: 'SecurePass123', minLength: 8, format: 'password' })
@IsString()
@MinLength(8)
password: string;
@ApiPropertyOptional({ example: 'SecurePass123', format: 'password' })
@IsOptional()
@ApiProperty({ example: 'SecurePass123', format: 'password' })
@IsString()
confirmPassword?: string;
@ApiPropertyOptional({ example: 'Ethiopian' })
@IsOptional()
@IsString()
nationality?: string;
@ApiPropertyOptional({ example: 'ET123456789' })
@IsOptional()
@IsString()
nationalId?: string;
@ApiPropertyOptional({ example: 'P1234567' })
@IsOptional()
@IsString()
passportNumber?: string;
confirmPassword: string;
}
export class LoginDto {

View File

@@ -13,7 +13,13 @@ import { EUserType } from '@tria-plc/api-common/utils/enums/user.enum';
import { PrismaService } from '../../common/prisma.service';
import { RegisterDto, LoginDto } from './auth.dto';
type IamUserRow = { id: string; name: { en: string; am: string } | null; phone_number: string | null };
type IamUserRow = {
id: string;
email: string;
name: { en: string; am: string } | null;
phone_number: string | null;
metadata: Record<string, any> | null;
};
@Injectable()
export class PassengerAuthService {
@@ -33,7 +39,7 @@ export class PassengerAuthService {
async register(dto: RegisterDto, req: any) {
const existing = await this.dataSource.query<{ id: string }[]>(
`SELECT id FROM iam.users WHERE email = $1 OR phone_number = $2 LIMIT 1`,
[dto.email, dto.phone],
[dto.email, dto.phoneNumber],
);
if (existing.length) throw new ConflictException('Email or phone already registered');
@@ -41,16 +47,16 @@ export class PassengerAuthService {
const { token, refreshToken } = await iamAuthService.signupWithPassword({
email: dto.email,
username: dto.email,
phoneNumber: dto.phone,
username: dto.username,
phoneNumber: dto.phoneNumber,
userType: EUserType.INDIVIDUAL,
name: { en: dto.fullName, am: dto.fullName },
name: dto.name,
password: dto.password,
confirmPassword: dto.confirmPassword ?? dto.password,
confirmPassword: dto.confirmPassword,
});
const iamRows = await this.dataSource.query<IamUserRow[]>(
`SELECT id, name, phone_number FROM iam.users WHERE email = $1 LIMIT 1`,
`SELECT id, email, name, phone_number, metadata FROM iam.users WHERE email = $1 LIMIT 1`,
[dto.email],
);
if (!iamRows.length) {
@@ -61,16 +67,7 @@ export class PassengerAuthService {
let passengerId: string;
try {
const result = await this.provisionPassengerSatellite({
iamUserId,
email: dto.email,
fullName: dto.fullName,
phone: dto.phone,
nationality: dto.nationality,
nationalId: dto.nationalId,
passportNumber: dto.passportNumber,
auditAction: 'USER_REGISTERED',
});
const result = await this.provisionPassengerSatellite({ iamUserId, auditAction: 'USER_REGISTERED' });
passengerId = result.passengerId;
} catch {
await this.compensateIamSignup(dto.email);
@@ -80,7 +77,7 @@ export class PassengerAuthService {
return {
token,
refreshToken,
user: { id: iamUserId, iamUserId, email: dto.email, fullName: dto.fullName, passengerId },
user: { id: iamUserId, iamUserId, email: dto.email, fullName: dto.name.en, passengerId },
};
}
@@ -102,7 +99,7 @@ export class PassengerAuthService {
const { token, refreshToken } = iamResult as { token: string; refreshToken: string };
const iamRows = await this.dataSource.query<IamUserRow[]>(
`SELECT id, name, phone_number FROM iam.users WHERE email = $1 LIMIT 1`,
`SELECT id, email, name, phone_number, metadata FROM iam.users WHERE email = $1 LIMIT 1`,
[dto.email],
);
const iamUser = iamRows[0];
@@ -117,12 +114,8 @@ export class PassengerAuthService {
});
if (!passenger) {
const fullName = iamUser.name?.en ?? iamUser.name?.am ?? dto.email;
const result = await this.provisionPassengerSatellite({
iamUserId: iamUser.id,
email: dto.email,
fullName,
phone: iamUser.phone_number ?? '',
auditAction: 'USER_AUTO_PROVISIONED',
});
passenger = { id: result.passengerId };
@@ -137,59 +130,11 @@ export class PassengerAuthService {
private async provisionPassengerSatellite(data: {
iamUserId: string;
email: string;
fullName: string;
phone: string;
nationality?: string;
nationalId?: string;
passportNumber?: string;
auditAction: string;
}): Promise<{ passengerId: string }> {
return this.prisma.$transaction(async (tx) => {
// Check if a local User already exists (pre-IAM registration)
const existingUser = await tx.user.findFirst({
where: { OR: [{ email: data.email }, { phone: data.phone }] },
select: { id: true },
});
if (existingUser) {
// User already exists — find their Passenger and stamp iamUserId
const existingPassenger = await tx.passenger.findFirst({
where: { userId: existingUser.id },
select: { id: true },
});
if (existingPassenger) {
await tx.passenger.update({
where: { id: existingPassenger.id },
data: { iamUserId: data.iamUserId },
});
return { passengerId: existingPassenger.id };
}
// User exists but no Passenger yet — create just the Passenger + sub-records
const passenger = await tx.passenger.create({
data: { userId: existingUser.id, iamUserId: data.iamUserId },
});
await tx.loyaltyAccount.create({ data: { passengerId: passenger.id } });
await tx.walletAccount.create({ data: { passengerId: passenger.id } });
return { passengerId: passenger.id };
}
// Brand new user — create the full satellite set
const user = await tx.user.create({
data: {
email: data.email,
phone: data.phone,
fullName: data.fullName,
passwordHash: 'IAM_MANAGED',
nationality: data.nationality,
nationalId: data.nationalId,
passportNumber: data.passportNumber,
},
});
const passenger = await tx.passenger.create({
data: { userId: user.id, iamUserId: data.iamUserId },
data: { iamUserId: data.iamUserId },
});
await tx.loyaltyAccount.create({ data: { passengerId: passenger.id } });
await tx.walletAccount.create({ data: { passengerId: passenger.id } });
@@ -200,7 +145,7 @@ export class PassengerAuthService {
action: data.auditAction,
entityType: 'User',
entityId: data.iamUserId,
newData: { email: data.email, iamUserId: data.iamUserId },
newData: { iamUserId: data.iamUserId },
},
});
return { passengerId: passenger.id };
@@ -214,26 +159,26 @@ export class PassengerAuthService {
}
async getProfile(iamUserId: string) {
const passenger = await this.prisma.passenger.findUnique({
where: { iamUserId },
include: {
user: true,
loyalty: true,
wallet: true,
},
});
if (!passenger) {
throw new Error('Passenger not found');
}
const [passenger, iamRows] = await Promise.all([
this.prisma.passenger.findUnique({
where: { iamUserId },
include: { loyalty: true, wallet: true },
}),
this.dataSource.query<IamUserRow[]>(
`SELECT id, email, name, phone_number, metadata FROM iam.users WHERE id = $1 LIMIT 1`,
[iamUserId],
),
]);
if (!passenger) throw new Error('Passenger not found');
const iam = iamRows[0];
return {
iamUserId,
email: passenger.user?.email,
phone: passenger.user?.phone,
fullName: passenger.user?.fullName,
nationality: passenger.user?.nationality,
nationalId: passenger.user?.nationalId,
passportNumber: passenger.user?.passportNumber,
faydaVerified: passenger.user?.faydaVerified,
email: iam?.email ?? null,
phone: iam?.phone_number ?? null,
fullName: iam?.name?.en ?? iam?.name?.am ?? null,
faydaVerified: iam?.metadata?.faydaVerified ?? false,
createdAt: passenger.createdAt,
passenger: {
id: passenger.id,