mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-27 10:52:53 +00:00
refactor(iam): remove remaining prisma.user refs from passenger side align RegisterDto to IAM body
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user