import { IsEmail, IsNotEmpty, IsString, IsStrongPassword, Length, ValidateNested, } from 'class-validator'; import { Type } from 'class-transformer'; import { ApiProperty } from '@nestjs/swagger'; export class NameDto { @ApiProperty({ example: 'ቀለሙ ቀጸላ' }) @IsString() am: string; @ApiProperty({ example: 'Kelemu Ketsela' }) @IsString() en: string; } export class RegisterDto { // Accepts an email OR a phone number. When a passenger signs up without an email, // the portal passes the phone number here (and as `username`) — the IAM only requires // a non-empty string, so a phone value is a valid account identifier. Kept as // @IsString/@IsNotEmpty (not @IsEmail) so that phone-as-email passes the ValidationPipe. @ApiProperty({ example: 'kelemu@email.com', description: 'Email or, when the user has no email, their phone number' }) @IsString() @IsNotEmpty() email: string; @ApiProperty({ example: 'kelemu.ketsela' }) @IsString() username: string; @ApiProperty({ example: '+251912345678' }) @IsString() phoneNumber: string; @ApiProperty({ type: NameDto }) @ValidateNested() @Type(() => NameDto) name: NameDto; } export class ResendRegistrationCodeDto { @ApiProperty({ example: 'kelemu@email.com' }) @IsEmail() email: string; @ApiProperty({ example: '+251912345678' }) @IsString() phoneNumber: string; } export class LoginDto { // Accepts an email OR a phone number in the same field. Passengers who registered // without an email log in with their phone number, which the IAM matches. Kept as // @IsString/@IsNotEmpty (not @IsEmail) so a phone value passes the ValidationPipe. @ApiProperty({ example: 'kelemu@email.com', description: 'Email or phone number' }) @IsString() @IsNotEmpty() email: string; @ApiProperty({ example: 'password123', format: 'password' }) @IsString() password: string; } export class FaydaRequestPasswordSetupDto { @ApiProperty({ example: '+251911234567', description: 'Phone number of the Fayda-verified account' }) @IsString() phoneNumber: string; } export class FaydaVerifyAndLoginDto { @ApiProperty({ example: '+251911234567' }) @IsString() phoneNumber: string; @ApiProperty({ example: '123456', description: '6-digit OTP received via SMS' }) @IsString() otp: string; } /** * Step 1 of the staged sign-in. One field: the passenger types either their phone number * or their email and the server decides which of the three branches follows. */ export class IdentifierLookupDto { @ApiProperty({ example: '+251912345678', description: 'Phone number or email address — the server detects which', }) @IsString() @IsNotEmpty() identifier: string; } /** Step 2a: ask for the SMS code that lets an account with no password set one. */ export class PasswordSetupRequestDto { @ApiProperty({ example: '+251912345678', description: 'Phone number or email address' }) @IsString() @IsNotEmpty() identifier: string; } /** Step 2b: redeem the code, set the password, and receive a session in one call. */ export class PasswordSetupCompleteDto { @ApiProperty({ example: '+251912345678', description: 'Phone number or email address' }) @IsString() @IsNotEmpty() identifier: string; @ApiProperty({ example: '123456', description: '6-digit code received via SMS' }) @IsString() @Length(4, 10) otp: string; // The credential is written directly against iam.user_credentials rather than through // the IAM's own set-password route, so the IAM's @IsStrongPassword rule has to be // restated here or weak passwords would slip in unvalidated. @ApiProperty({ example: 'Str0ng!Pass', format: 'password' }) @IsStrongPassword({ minLength: 8, minLowercase: 1, minUppercase: 1, minNumbers: 1, minSymbols: 1, }) newPassword: string; @ApiProperty({ example: 'Str0ng!Pass', format: 'password' }) @IsString() confirmPassword: string; }