register.ts (1971B)
1 import { PhoneRepository } from "#core/application/phone/phone_repository.ts"; 2 import { EntityLocked } from "#core/application/repository_error.ts"; 3 import { AlreadyVerifiedCodeChallenge } from "#core/domain/code_challenge.ts"; 4 import { ExceedingLimit } from "#core/domain/limiter.ts"; 5 import { 6 InvalidPersonalPhoneNumber, 7 PersonalPhoneNumber, 8 } from "#core/domain/personal_phone_number.ts"; 9 import { UUID } from "#core/domain/uuid.ts"; 10 11 export type PhoneRegisterRequest = { 12 uuid: string; 13 phoneNumber: string; 14 }; 15 16 export type PhoneRegisterResponse = { 17 status: "sent" | "invalid" | "conflict" | "verified"; 18 delay: number; 19 }; 20 21 export interface PhoneSmsChallengeSender { 22 send(phoneNumber: string, code: string): Promise<void> | void; 23 } 24 25 export class PhoneRegisterUseCase { 26 constructor( 27 private readonly phoneRepo: PhoneRepository, 28 private readonly sender: PhoneSmsChallengeSender, 29 ) { 30 } 31 32 async execute( 33 request: PhoneRegisterRequest, 34 ): Promise<PhoneRegisterResponse> { 35 try { 36 const uuid = new UUID(request.uuid); 37 const phoneNumber = new PersonalPhoneNumber(request.phoneNumber); 38 const phone = await this.phoneRepo.findOrCreate(uuid); 39 const code = phone.requestSmsChallenge(phoneNumber); 40 await this.phoneRepo.store(phone); 41 await this.sender.send( 42 phoneNumber.toString(), 43 code.toString(), 44 ); 45 return { 46 status: "sent", 47 delay: phone.smsChallenge.requestDelay, 48 }; 49 } catch (error) { 50 if (error instanceof AlreadyVerifiedCodeChallenge) { 51 return { status: "verified", delay: 0 }; 52 } 53 if (error instanceof InvalidPersonalPhoneNumber) { 54 return { status: "invalid", delay: 0 }; 55 } 56 if (error instanceof EntityLocked) { 57 return { status: "conflict", delay: 0 }; 58 } 59 if (error instanceof ExceedingLimit) { 60 return { status: "sent", delay: error.delay }; 61 } 62 throw error; 63 } 64 } 65 }