import { BadRequestException, forwardRef, Inject, Injectable, Logger } from '@nestjs/common'; import { FindOptionsWhere } from 'typeorm'; import { Transactional } from 'typeorm-transactional'; import { CustomerService } from '~/customer/services'; import { CreateUnverifiedUserRequestDto } from '../../auth/dtos/request'; import { Roles } from '../../auth/enums'; import { User } from '../entities'; import { UserRepository } from '../repositories'; @Injectable() export class UserService { private readonly logger = new Logger(UserService.name); constructor( private readonly userRepository: UserRepository, @Inject(forwardRef(() => CustomerService)) private readonly customerService: CustomerService, ) {} findUser(where: FindOptionsWhere | FindOptionsWhere[]) { this.logger.log(`finding user with where clause ${JSON.stringify(where)}`); return this.userRepository.findOne(where); } async findUserOrThrow(where: FindOptionsWhere) { this.logger.log(`Finding user with where clause ${JSON.stringify(where)}`); const user = await this.findUser(where); if (!user) { this.logger.error(`User with not found with where clause ${JSON.stringify(where)}`); throw new BadRequestException('USER.NOT_FOUND'); } this.logger.log(`User with where clause ${JSON.stringify(where)} found successfully`); return user; } async findOrCreateUser({ phoneNumber, countryCode }: CreateUnverifiedUserRequestDto) { this.logger.log(`Finding or creating user with phone number ${phoneNumber} and country code ${countryCode}`); const user = await this.userRepository.findOne({ phoneNumber }); if (!user) { this.logger.log(`User with phone number ${phoneNumber} not found, creating new user`); return this.userRepository.createUnverifiedUser({ phoneNumber, countryCode, roles: [Roles.GUARDIAN] }); } if (user && user.roles.includes(Roles.GUARDIAN) && user.isProfileCompleted) { this.logger.error(`User with phone number ${phoneNumber} already exists`); throw new BadRequestException('USER.PHONE_NUMBER_ALREADY_EXISTS'); } if (user && user.roles.includes(Roles.JUNIOR)) { this.logger.error(`User with phone number ${phoneNumber} is an already registered junior`); throw new BadRequestException('USER.JUNIOR_UPGRADE_NOT_SUPPORTED_YET'); //TODO add role Guardian to the existing user and send OTP } this.logger.log(`User with phone number ${phoneNumber} and country code ${countryCode} found successfully`); return user; } async createUser(data: Partial) { this.logger.log(`Creating user with data ${JSON.stringify(data)}`); const user = await this.userRepository.createUser(data); this.logger.log(`User with data ${JSON.stringify(data)} created successfully`); return user; } setEmail(userId: string, email: string) { this.logger.log(`Setting email ${email} for user ${userId}`); return this.userRepository.update(userId, { email }); } setPasscode(userId: string, passcode: string, salt: string) { this.logger.log(`Setting passcode for user ${userId}`); return this.userRepository.update(userId, { password: passcode, salt, isProfileCompleted: true }); } setPhoneNumber(userId: string, phoneNumber: string, countryCode: string) { this.logger.log(`Setting phone number ${phoneNumber} for user ${userId}`); return this.userRepository.update(userId, { phoneNumber, countryCode }); } verifyPhoneNumber(userId: string) { this.logger.log(`Verifying phone number for user ${userId}`); return this.userRepository.update(userId, { isPhoneVerified: true }); } @Transactional() async createGoogleUser(googleId: string, email: string) { this.logger.log(`Creating google user with googleId ${googleId} and email ${email}`); const user = await this.userRepository.createUser({ googleId, email, roles: [Roles.GUARDIAN], isEmailVerified: true, }); await this.customerService.createGuardianCustomer(user.id); return this.findUserOrThrow({ id: user.id }); } @Transactional() async createAppleUser(appleId: string, email: string) { this.logger.log(`Creating apple user with appleId ${appleId} and email ${email}`); const user = await this.userRepository.createUser({ appleId, email, roles: [Roles.GUARDIAN], isEmailVerified: true, }); await this.customerService.createGuardianCustomer(user.id); return this.findUserOrThrow({ id: user.id }); } @Transactional() async verifyUserAndCreateCustomer(userId: string) { this.logger.log(`Verifying user ${userId} and creating customer`); await this.userRepository.update(userId, { isPhoneVerified: true }); await this.customerService.createGuardianCustomer(userId); this.logger.log(`User ${userId} verified and customer created successfully`); return this.findUserOrThrow({ id: userId }); } }