712 lines
26 KiB
TypeScript
Executable File
712 lines
26 KiB
TypeScript
Executable File
import { createHmac, randomBytes } from "node:crypto";
|
|
|
|
import { BadRequestException, HttpStatus, Injectable, Logger } from "@nestjs/common";
|
|
import { ConfigService } from "@nestjs/config";
|
|
import { FastifyReply } from "fastify";
|
|
import slugify from "slugify";
|
|
import { In, Not, QueryRunner } from "typeorm";
|
|
|
|
import { AdminMessage, AdsMessage, AuthMessage, CommonMessage, UserMessage } from "../../../common/enums/message.enum";
|
|
import { AddressService } from "../../address/providers/address.service";
|
|
import { CompleteRegistrationDto } from "../../auth/DTO/complete-register.dto";
|
|
import { RequestOtpDto } from "../../auth/DTO/request-otp.dto";
|
|
import { VerifyOtpDto } from "../../auth/DTO/verify-otp.dto";
|
|
import { UserSettingsService } from "../../settings/providers/user-settings.service";
|
|
import { CacheService } from "../../utils/providers/cache.service";
|
|
import { EmailService } from "../../utils/providers/email.service";
|
|
import { OTPService } from "../../utils/providers/otp.service";
|
|
import { PaginationUtils } from "../../utils/providers/pagination.utils";
|
|
import { PasswordService } from "../../utils/providers/password.service";
|
|
import { SmsService } from "../../utils/providers/sms.service";
|
|
import { WalletsService } from "../../wallets/providers/wallets.service";
|
|
import { ChangeEmailDto } from "../DTO/change-email.dto";
|
|
import { CheckValidityDTO } from "../DTO/check-validity.dto";
|
|
import { CreateAdminDto } from "../DTO/create-admin.dto";
|
|
import { CreateLegalUserDto } from "../DTO/create-legal-user.dto";
|
|
import { CreateRealUserDto } from "../DTO/create-real-user.dto";
|
|
import { CreateRoleDto } from "../DTO/create-role.dto";
|
|
import { SearchAdminQueryDto } from "../DTO/search-admins-query.dto";
|
|
import { SearchCustomersDto } from "../DTO/search-customers.dto";
|
|
import { SearchRolesQueryDto } from "../DTO/search-roles.dto";
|
|
import { UpdateProfileDto } from "../DTO/update-profile.dto";
|
|
import { CreateUserGroupDto } from "../DTO/user-group.dto";
|
|
import { EmailVerifyQueryDto } from "../DTO/verify-email-query.dto";
|
|
import { Role } from "../entities/role.entity";
|
|
import { User } from "../entities/user.entity";
|
|
import { FinancialType } from "../enums/financial-type.enum";
|
|
import { RoleEnum } from "../enums/role.enum";
|
|
import { ValidityType } from "../enums/validity-type.enum";
|
|
import { LegalUserRepository } from "../repositories/legal-user.repository";
|
|
import { PermissionsRepository } from "../repositories/permissions.repository";
|
|
import { RealUserRepository } from "../repositories/real-user.repository";
|
|
import { RoleRepository } from "../repositories/roles.repository";
|
|
import { UserGroupRepository } from "../repositories/user-group.repository";
|
|
import { UserRepository } from "../repositories/users.repository";
|
|
|
|
@Injectable()
|
|
export class UsersService {
|
|
private readonly logger = new Logger(UsersService.name);
|
|
constructor(
|
|
private readonly userRepository: UserRepository,
|
|
private readonly userGroupRepository: UserGroupRepository,
|
|
private readonly userSettingsService: UserSettingsService,
|
|
private readonly walletsService: WalletsService,
|
|
private readonly rolesRepository: RoleRepository,
|
|
private readonly permissionsRepository: PermissionsRepository,
|
|
private readonly passwordService: PasswordService,
|
|
private readonly addressService: AddressService,
|
|
private readonly realUserRepository: RealUserRepository,
|
|
private readonly legalUserRepository: LegalUserRepository,
|
|
private readonly emailService: EmailService,
|
|
private readonly configService: ConfigService,
|
|
private readonly otpService: OTPService,
|
|
private readonly smsService: SmsService,
|
|
private readonly cacheService: CacheService,
|
|
) {}
|
|
|
|
/************************************************************ */
|
|
|
|
async findOneById(id: string) {
|
|
const user = await this.userRepository.findOneBy({ id });
|
|
if (!user) throw new BadRequestException(UserMessage.USER_NOT_FOUND);
|
|
return { user };
|
|
}
|
|
/************************************************************ */
|
|
|
|
async findOneByIdWithQueryRunner(id: string, queryRunner: QueryRunner) {
|
|
const user = await queryRunner.manager.findOneBy(User, { id });
|
|
if (!user) throw new BadRequestException(UserMessage.USER_NOT_FOUND);
|
|
return user;
|
|
}
|
|
|
|
/************************************************************ */
|
|
|
|
async getMe(userId: string) {
|
|
const user = await this.userRepository.findOne({ where: { id: userId }, relations: { roles: true } });
|
|
if (!user) throw new BadRequestException(UserMessage.USER_NOT_FOUND);
|
|
return { user };
|
|
}
|
|
|
|
/************************************************************ */
|
|
|
|
async getRealUserData(userId: string) {
|
|
const realUser = await this.realUserRepository.findOne({
|
|
where: { user: { id: userId } },
|
|
relations: { address: { city: { province: true } } },
|
|
});
|
|
return { realUser };
|
|
}
|
|
/************************************************************ */
|
|
|
|
async getLegalUserData(userId: string) {
|
|
const legalUser = await this.legalUserRepository.findOne({
|
|
where: { user: { id: userId } },
|
|
relations: { address: { city: { province: true } } },
|
|
});
|
|
return { legalUser };
|
|
}
|
|
/************************************************************ */
|
|
|
|
async updateProfile(userId: string, updateProfileDto: UpdateProfileDto) {
|
|
let city;
|
|
|
|
const user = await this.userRepository.findOneBy({ id: userId });
|
|
if (!user) throw new BadRequestException(UserMessage.USER_NOT_FOUND);
|
|
|
|
if (updateProfileDto.cityId) {
|
|
const { city: existCity } = await this.addressService.getCity(+updateProfileDto.cityId);
|
|
if (!existCity) throw new BadRequestException(UserMessage.CITY_NOT_FOUND);
|
|
city = existCity;
|
|
}
|
|
|
|
//
|
|
if (updateProfileDto.userName) {
|
|
updateProfileDto.userName = slugify(updateProfileDto.userName, { lower: true, trim: true });
|
|
const existUserName = await this.userRepository.findOneBy({ userName: updateProfileDto.userName, id: Not(userId) });
|
|
if (existUserName) throw new BadRequestException(UserMessage.USERNAME_EXIST);
|
|
}
|
|
|
|
await this.userRepository.save({ ...user, ...updateProfileDto, city });
|
|
//
|
|
return {
|
|
message: CommonMessage.UPDATE_SUCCESS,
|
|
};
|
|
}
|
|
/************************************************************ */
|
|
|
|
async changeEmail(userId: string, changeDto: ChangeEmailDto) {
|
|
const { email } = changeDto;
|
|
const user = await this.userRepository.findOneBy({ id: userId });
|
|
if (!user) throw new BadRequestException(UserMessage.USER_NOT_FOUND);
|
|
|
|
const existEmail = await this.userRepository.findOneBy({ email, id: Not(userId) });
|
|
if (existEmail) throw new BadRequestException(UserMessage.EMAIL_EXIST);
|
|
|
|
const emailLink = await this.createEmailHashLink(email);
|
|
|
|
await this.emailService.sendVerificationEmail(email, emailLink, `${user.firstName} ${user.lastName}`);
|
|
|
|
if (user.email !== email) {
|
|
user.email = email;
|
|
user.emailVerified = false;
|
|
}
|
|
await this.userRepository.save(user);
|
|
|
|
return {
|
|
message: UserMessage.VERIFY_EMAIL_LINK_SENT,
|
|
email,
|
|
};
|
|
}
|
|
/************************************************************ */
|
|
|
|
async verifyEmail(queryDto: EmailVerifyQueryDto, rep: FastifyReply) {
|
|
const frontUrl = this.configService.getOrThrow<string>("SITE_URL");
|
|
|
|
const { token, email } = queryDto;
|
|
|
|
const user = await this.userRepository.findOneBy({ email, emailVerified: true });
|
|
if (user) throw new BadRequestException(UserMessage.EMAIL_EXIST);
|
|
|
|
const cachedData = await this.cacheService.get(`EMAIL_VERIFY:${email}`);
|
|
if (!cachedData) throw new BadRequestException(UserMessage.INVALID_EMAIL_TOKEN);
|
|
|
|
const { hash, nonce } = JSON.parse(cachedData as string);
|
|
|
|
const secret = this.configService.getOrThrow<string>("EMAIL_SECRET");
|
|
|
|
if (hash !== token) throw new BadRequestException(UserMessage.INVALID_EMAIL_TOKEN);
|
|
|
|
const expectedHash = createHmac("sha256", secret).update(`${email}:${nonce}`).digest("hex");
|
|
if (expectedHash !== hash) throw new BadRequestException(UserMessage.INVALID_EMAIL_TOKEN);
|
|
|
|
const userToUpdate = await this.userRepository.findOneBy({ email });
|
|
if (!userToUpdate) throw new BadRequestException(UserMessage.USER_NOT_FOUND);
|
|
|
|
userToUpdate.emailVerified = true;
|
|
await this.userRepository.save(userToUpdate);
|
|
|
|
await this.cacheService.del(`EMAIL_VERIFY:${email}`);
|
|
|
|
return rep.status(HttpStatus.FOUND).redirect(`${frontUrl}/profile`);
|
|
}
|
|
/************************************************************ */
|
|
|
|
async changePhone(userId: string, requestOtpDto: RequestOtpDto) {
|
|
const { phone } = requestOtpDto;
|
|
const user = await this.userRepository.findOneBy({ id: userId });
|
|
if (!user) throw new BadRequestException(UserMessage.USER_NOT_FOUND);
|
|
|
|
const existPhone = await this.userRepository.findOneBy({ phone });
|
|
if (existPhone) throw new BadRequestException(UserMessage.PHONE_EXIST);
|
|
|
|
const existCode = await this.otpService.checkExistOtp(phone, "LOGIN");
|
|
if (existCode) {
|
|
return {
|
|
message: AuthMessage.OTP_ALREADY_SENT,
|
|
ttlSecond: existCode,
|
|
};
|
|
}
|
|
|
|
const otpCode = await this.otpService.generateAndSetInCache(phone, "LOGIN");
|
|
//
|
|
await this.smsService.sendSmsVerifyCode(phone, otpCode);
|
|
this.logger.debug(`OTP sent to ${phone}: ${otpCode}`);
|
|
|
|
return {
|
|
message: AuthMessage.OTP_SENT,
|
|
otpCode,
|
|
};
|
|
}
|
|
|
|
/************************************************************ */
|
|
|
|
async verifyPhone(verifyOtpDto: VerifyOtpDto, userId: string) {
|
|
const { code, phone } = verifyOtpDto;
|
|
|
|
const user = await this.checkUserLoginCredentialWithPhone(phone, code, userId);
|
|
|
|
user.phone = phone;
|
|
await this.userRepository.save(user);
|
|
|
|
return {
|
|
message: CommonMessage.UPDATE_SUCCESS,
|
|
};
|
|
}
|
|
|
|
/************************************************************ */
|
|
|
|
async findOneWithEmail(email: string): Promise<User | null> {
|
|
const user = await this.userRepository.findOneWithEmail(email);
|
|
return user;
|
|
}
|
|
/************************************************************ */
|
|
|
|
async findOneWithPhone(phone: string): Promise<User | null> {
|
|
const user = await this.userRepository.findOneWithPhone(phone);
|
|
return user;
|
|
}
|
|
|
|
/************************************************************ */
|
|
|
|
async createUser(registerDto: CompleteRegistrationDto, hashedPassword: string, queryRunner: QueryRunner): Promise<User> {
|
|
const role = await queryRunner.manager.findOneBy(Role, { name: RoleEnum.USER });
|
|
if (!role) throw new BadRequestException(UserMessage.ROLE_NOT_FOUND);
|
|
|
|
const existUser = await queryRunner.manager.findOneBy(User, { nationalCode: registerDto.nationalCode });
|
|
if (existUser) throw new BadRequestException(UserMessage.NATIONAL_CODE_EXIST);
|
|
|
|
const user = queryRunner.manager.create(User, {
|
|
...registerDto,
|
|
password: hashedPassword,
|
|
roles: [role],
|
|
});
|
|
await queryRunner.manager.save(user);
|
|
|
|
await this.userSettingsService.createUserSettings(user.id, queryRunner);
|
|
|
|
await this.walletsService.createUserWallet(user.id, queryRunner);
|
|
|
|
return user;
|
|
}
|
|
|
|
/************************************************************ */
|
|
|
|
async createAdmin(createDto: CreateAdminDto) {
|
|
const existEmail = await this.userRepository.findOneBy({ email: createDto.email });
|
|
if (existEmail) throw new BadRequestException(UserMessage.EMAIL_EXIST);
|
|
|
|
const existPhone = await this.userRepository.findOneBy({ phone: createDto.phone });
|
|
if (existPhone) throw new BadRequestException(UserMessage.PHONE_EXIST);
|
|
|
|
const role = await this.rolesRepository.findOne({ where: { id: createDto.roleId }, relations: { permissions: true } });
|
|
if (!role) throw new BadRequestException(AdsMessage.ROLE_NOT_FOUND);
|
|
|
|
await this.rolesRepository.save(role);
|
|
|
|
if (createDto.password !== createDto.repeatPassword) throw new BadRequestException(AdminMessage.PASSWORD_NOT_MATCH);
|
|
|
|
const hashedPassword = await this.passwordService.hashPassword(createDto.password);
|
|
|
|
const userName = slugify(`${createDto.firstName} ${Date.now().toString().slice(-5)}`, { lower: true, trim: true });
|
|
|
|
const adminUser = this.userRepository.create({ ...createDto, roles: [role], password: hashedPassword, userName });
|
|
await this.userRepository.save(adminUser);
|
|
return {
|
|
message: AdminMessage.ADMIN_CREATED,
|
|
adminUser,
|
|
};
|
|
}
|
|
/************************************************************ */
|
|
|
|
async createRole(createDto: CreateRoleDto) {
|
|
const existRole = await this.rolesRepository.findOne({ where: { name: createDto.name } });
|
|
if (existRole) throw new BadRequestException(AdminMessage.ROLE_EXIST);
|
|
|
|
const permissions = await this.permissionsRepository.findBy({ id: In(createDto.permissions) });
|
|
if (permissions.length !== createDto.permissions.length) throw new BadRequestException(AdminMessage.PERMISSIONS_NOT_FOUND);
|
|
|
|
const role = this.rolesRepository.create({ ...createDto, permissions, isAdmin: true });
|
|
await this.rolesRepository.save(role);
|
|
return {
|
|
message: AdminMessage.ROLE_CREATED,
|
|
role,
|
|
};
|
|
}
|
|
|
|
/************************************************************ */
|
|
|
|
async checkValidity(checkDto: CheckValidityDTO, userId: string) {
|
|
const { type } = checkDto;
|
|
if (type === ValidityType.USERNAME) checkDto.value = slugify(checkDto.value, { lower: true, trim: true });
|
|
|
|
const user = await this.userRepository.findOneBy({ [type]: checkDto.value, id: Not(userId) });
|
|
if (user) throw new BadRequestException(UserMessage.USER_EXISTS);
|
|
|
|
return { message: CommonMessage.VALID_FOR_CHOOSE };
|
|
}
|
|
/************************************************************ */
|
|
|
|
async createUserGroup(createUserGroupDto: CreateUserGroupDto) {
|
|
const users = await this.userRepository.find({ where: { id: In(createUserGroupDto.userIds) } });
|
|
const userGroup = this.userGroupRepository.create({
|
|
...createUserGroupDto,
|
|
users,
|
|
});
|
|
await this.userGroupRepository.save(userGroup);
|
|
return {
|
|
message: UserMessage.USER_GROUP_CREATED,
|
|
};
|
|
}
|
|
///****************************************************** */
|
|
|
|
async getUserGroups() {
|
|
const usersGroup = await this.userGroupRepository.find({ relations: { users: true } });
|
|
return {
|
|
usersGroup,
|
|
};
|
|
}
|
|
|
|
///****************************************************** */
|
|
|
|
async getUserGroupById(id: string) {
|
|
const userGroup = await this.userGroupRepository.findGroupById(id);
|
|
if (!userGroup) throw new BadRequestException(UserMessage.USER_GROUP_NOT_FOUND);
|
|
return {
|
|
userGroup,
|
|
};
|
|
}
|
|
|
|
///****************************************************** */
|
|
|
|
async getUserGroupsByIds(ids: string[]) {
|
|
const userGroups = await this.userGroupRepository.find({ where: { id: In(ids) } });
|
|
if (!userGroups) throw new BadRequestException(UserMessage.USER_GROUP_NOT_FOUND);
|
|
return {
|
|
userGroups,
|
|
};
|
|
}
|
|
|
|
///****************************************************** */
|
|
|
|
async findUsersByIds(ids: string[]) {
|
|
const users = await this.userRepository.find({ where: { id: In(ids) } });
|
|
return users;
|
|
}
|
|
|
|
///****************************************************** */
|
|
|
|
async updateUserPassword(userId: string, newPassword: string) {
|
|
const updatedUser = await this.userRepository.update(
|
|
{ id: userId },
|
|
{
|
|
password: newPassword,
|
|
},
|
|
);
|
|
return {
|
|
updatedUser,
|
|
};
|
|
}
|
|
|
|
/************************************************************ */
|
|
|
|
async findAllUsers() {
|
|
const users = await this.userRepository.find({
|
|
relations: ["groups"],
|
|
});
|
|
|
|
return { users };
|
|
}
|
|
|
|
/************************************************************ */
|
|
|
|
async getCustomers(queryDto: SearchCustomersDto) {
|
|
const { limit, skip } = PaginationUtils(queryDto);
|
|
|
|
const queryBuilder = this.userRepository
|
|
.createQueryBuilder("user")
|
|
.leftJoin("user.roles", "role")
|
|
.where("role.name = :roleName", { roleName: RoleEnum.USER })
|
|
.leftJoin("user.groups", "groups")
|
|
.addSelect(["groups.id", "groups.name"])
|
|
.loadRelationCountAndMap("user.invoicesCount", "user.invoices")
|
|
.loadRelationCountAndMap("user.subscriptionsCount", "user.subscriptions")
|
|
.loadRelationCountAndMap("user.ticketsCount", "user.tickets");
|
|
|
|
if (queryDto.q) {
|
|
queryBuilder
|
|
.orWhere("user.firstName ILIKE :search", { search: `%${queryDto.q}%` })
|
|
.orWhere("user.lastName ILIKE :search", { search: `%${queryDto.q}%` })
|
|
.orWhere("user.userName ILIKE :search", { search: `%${queryDto.q}%` });
|
|
}
|
|
|
|
const [customers, count] = await queryBuilder.skip(skip).take(limit).getManyAndCount();
|
|
|
|
return { customers, count };
|
|
}
|
|
|
|
/************************************************************ */
|
|
|
|
// async createCustomer(createDto: CreateCustomerDto) {
|
|
// const { cityId, address, postalCode, economicCode, companyRegisteredName, registrationId, nationalId, number, ...userData } = createDto;
|
|
|
|
// const queryRunner = this.userRepository.manager.connection.createQueryRunner();
|
|
// await queryRunner.connect();
|
|
// await queryRunner.startTransaction();
|
|
|
|
// try {
|
|
// const existUser = await queryRunner.manager.findOneBy(User, { email: createDto.email });
|
|
// if (existUser) throw new BadRequestException(UserMessage.EMAIL_EXIST);
|
|
|
|
// const role = await queryRunner.manager.findOneBy(Role, { name: RoleEnum.USER });
|
|
// if (!role) throw new BadRequestException(UserMessage.ROLE_NOT_FOUND);
|
|
|
|
// const hashedPassword = await this.passwordService.hashPassword(createDto.password);
|
|
|
|
// const legalUserData = economicCode
|
|
// ? {
|
|
// economicCode,
|
|
// companyRegisteredName,
|
|
// registrationId,
|
|
// nationalId,
|
|
// number,
|
|
// address: {
|
|
// address,
|
|
// city: cityId
|
|
// ? {
|
|
// id: +cityId,
|
|
// }
|
|
// : undefined,
|
|
// postalCode,
|
|
// },
|
|
// }
|
|
// : undefined;
|
|
|
|
// const user = queryRunner.manager.create(User, {
|
|
// ...userData,
|
|
// password: hashedPassword,
|
|
// roles: [role],
|
|
// legalUser: legalUserData,
|
|
// });
|
|
// await queryRunner.manager.save(user);
|
|
|
|
// await this.userSettingsService.createUserSettings(user.id, queryRunner);
|
|
|
|
// await this.walletsService.createUserWallet(user.id, queryRunner);
|
|
|
|
// await queryRunner.commitTransaction();
|
|
|
|
// return {
|
|
// message: CommonMessage.CREATED,
|
|
// user,
|
|
// };
|
|
// } catch (error) {
|
|
// await queryRunner.rollbackTransaction();
|
|
// throw error;
|
|
// } finally {
|
|
// await queryRunner.release();
|
|
// }
|
|
// }
|
|
|
|
/************************************************************ */
|
|
|
|
async findOneCustomer(userId: string) {
|
|
const customer = await this.userRepository.findOne({
|
|
where: { id: userId, roles: { name: RoleEnum.USER } },
|
|
relations: { legalUser: true, realUser: true },
|
|
});
|
|
|
|
return { customer };
|
|
}
|
|
/************************************************************ */
|
|
|
|
async getUsersByGroup(groupId: string) {
|
|
const users = await this.userRepository.find({ where: { groups: { id: groupId } }, relations: { tickets: true } });
|
|
return { users };
|
|
}
|
|
/************************************************************ */
|
|
|
|
async getAdmins(queryDto: SearchAdminQueryDto) {
|
|
const { limit, skip } = PaginationUtils(queryDto);
|
|
const queryBuilder = this.userRepository.createQueryBuilder("user");
|
|
|
|
queryBuilder
|
|
.leftJoinAndSelect("user.roles", "role")
|
|
// .addSelect(["role.id", "role.name"])
|
|
.where("role.isAdmin = :isAdmin", { isAdmin: true })
|
|
.leftJoinAndSelect("user.groups", "groups")
|
|
.leftJoinAndSelect("role.permissions", "permissions");
|
|
// .where("permissions.name IN (:...adminPermissions)", {
|
|
// adminPermissions: [PermissionEnum.ADMINS],
|
|
// });
|
|
// .addSelect(["permissions.id", "permissions.name"]);
|
|
|
|
if (queryDto.q) {
|
|
queryBuilder.andWhere("user.firstName ILIKE :q OR user.lastName ILIKE :q OR user.userName ILIKE :q", {
|
|
q: `%${queryDto.q}%`,
|
|
});
|
|
}
|
|
|
|
if (queryDto.permission) {
|
|
queryBuilder.andWhere("permissions.name = :permName", { permName: queryDto.permission });
|
|
}
|
|
|
|
queryBuilder.take(limit).skip(skip).orderBy("user.createdAt", "DESC");
|
|
|
|
const [users, count] = await queryBuilder.getManyAndCount();
|
|
|
|
return {
|
|
users,
|
|
count,
|
|
paginate: true,
|
|
};
|
|
}
|
|
|
|
/************************************************************ */
|
|
|
|
async getPermissions() {
|
|
const permissions = await this.permissionsRepository.find({ select: { id: true, name: true } });
|
|
return {
|
|
permissions,
|
|
};
|
|
}
|
|
/************************************************************ */
|
|
|
|
async getRoles(queryDto: SearchRolesQueryDto) {
|
|
const { limit, skip } = PaginationUtils(queryDto);
|
|
const queryBuilder = this.rolesRepository.createQueryBuilder("role");
|
|
|
|
queryBuilder
|
|
.where("role.isAdmin = :isAdmin", { isAdmin: true })
|
|
// .leftJoinAndSelect("role.permissions", "permissions")
|
|
.loadRelationCountAndMap("role.userCount", "role.users");
|
|
|
|
if (queryDto.q) {
|
|
queryBuilder.andWhere("role.name ILIKE :q", { q: `%${queryDto.q}%` });
|
|
}
|
|
|
|
queryBuilder.take(limit).skip(skip).orderBy("role.createdAt", "DESC");
|
|
|
|
const [roles, count] = await queryBuilder.getManyAndCount();
|
|
|
|
return {
|
|
roles,
|
|
count,
|
|
paginate: true,
|
|
};
|
|
}
|
|
/************************************************************ */
|
|
//TODO:query manager
|
|
async createRealUserData(createDto: CreateRealUserDto, userId: string) {
|
|
const user = await this.userRepository.findOneBy({ id: userId });
|
|
if (!user) throw new BadRequestException(UserMessage.USER_NOT_FOUND);
|
|
|
|
if (user.financialType && user.financialType === FinancialType.LEGAL)
|
|
throw new BadRequestException(UserMessage.FINANCIAL_TYPE_IS_LEGAL);
|
|
//
|
|
const existRealUser = await this.realUserRepository.findOne({ where: { user: { id: user.id } } });
|
|
|
|
if (existRealUser) throw new BadRequestException(UserMessage.REAL_DATA_EXIST);
|
|
|
|
const { city } = await this.addressService.getCity(+createDto.cityId);
|
|
|
|
const existNationalCode = await this.realUserRepository.findOne({ where: { nationalCode: createDto.nationalCode } });
|
|
if (existNationalCode) throw new BadRequestException(UserMessage.NATIONAL_CODE_EXIST);
|
|
|
|
const existPhone = await this.realUserRepository.findOne({ where: { phone: createDto.phone } });
|
|
if (existPhone) throw new BadRequestException(UserMessage.PHONE_EXIST);
|
|
|
|
const realUser = this.realUserRepository.create({
|
|
...createDto,
|
|
user,
|
|
address: {
|
|
fullAddress: createDto.address,
|
|
city,
|
|
postalCode: createDto.postalCode,
|
|
},
|
|
});
|
|
|
|
await this.realUserRepository.save(realUser);
|
|
|
|
user.financialType = FinancialType.REAL;
|
|
await this.userRepository.save(user);
|
|
|
|
return {
|
|
message: CommonMessage.CREATED,
|
|
realUser,
|
|
};
|
|
}
|
|
|
|
// /************************************************************ */
|
|
|
|
//TODO:query manager
|
|
async createLegalUserData(createDto: CreateLegalUserDto, userId: string) {
|
|
const user = await this.userRepository.findOneBy({ id: userId });
|
|
if (!user) throw new BadRequestException(UserMessage.USER_NOT_FOUND);
|
|
|
|
if (user.financialType && user.financialType === FinancialType.REAL) throw new BadRequestException(UserMessage.FINANCIAL_TYPE_IS_REAL);
|
|
|
|
const existingLegalUser = await this.legalUserRepository.findOne({ where: { user: { id: user.id } } });
|
|
|
|
if (existingLegalUser) throw new BadRequestException(UserMessage.LEGAL_DATA_EXIST);
|
|
|
|
const existEconomicCode = await this.legalUserRepository.findOne({ where: { economicCode: createDto.economicCode } });
|
|
if (existEconomicCode) throw new BadRequestException(UserMessage.ECONOMIC_CODE_EXIST);
|
|
|
|
const existPhone = await this.legalUserRepository.findOne({ where: { phone: createDto.phone } });
|
|
if (existPhone) throw new BadRequestException(UserMessage.PHONE_EXIST);
|
|
|
|
const existRegistrationCode = await this.legalUserRepository.findOne({ where: { registrationCode: createDto.registrationCode } });
|
|
if (existRegistrationCode) throw new BadRequestException(UserMessage.REGISTRATION_CODE_EXIST);
|
|
|
|
const existNationalIdentity = await this.legalUserRepository.findOne({ where: { nationalIdentity: createDto.nationalIdentity } });
|
|
if (existNationalIdentity) throw new BadRequestException(UserMessage.NATIONAL_IDENTITY_EXIST);
|
|
|
|
const { city } = await this.addressService.getCity(+createDto.cityId);
|
|
|
|
const legalUser = this.legalUserRepository.create({
|
|
...createDto,
|
|
user,
|
|
address: {
|
|
fullAddress: createDto.address,
|
|
city,
|
|
postalCode: createDto.postalCode,
|
|
},
|
|
});
|
|
|
|
await this.legalUserRepository.save(legalUser);
|
|
|
|
user.financialType = FinancialType.LEGAL;
|
|
await this.userRepository.save(user);
|
|
|
|
return {
|
|
message: CommonMessage.CREATED,
|
|
legalUser,
|
|
};
|
|
}
|
|
|
|
/************************************************************ */
|
|
|
|
async getCustomersCount() {
|
|
const count = await this.userRepository.count({
|
|
where: {
|
|
roles: {
|
|
name: RoleEnum.USER,
|
|
},
|
|
},
|
|
});
|
|
return count;
|
|
}
|
|
|
|
/************************************************************ */
|
|
|
|
private async createEmailHashLink(email: string) {
|
|
const secret = this.configService.getOrThrow<string>("EMAIL_SECRET");
|
|
const nonce = randomBytes(16).toString("hex");
|
|
const hash = createHmac("sha256", secret).update(`${email}:${nonce}`).digest("hex");
|
|
|
|
const url = new URL(`${this.configService.getOrThrow<string>("API_URL")}/users/verify-email`);
|
|
url.searchParams.append("token", hash);
|
|
url.searchParams.append("email", email);
|
|
|
|
await this.cacheService.set(`EMAIL_VERIFY:${email}`, JSON.stringify({ hash, email, nonce }), 5 * 60 * 1000);
|
|
|
|
return url.toString();
|
|
}
|
|
|
|
/************************************************************ */
|
|
|
|
private async checkUserLoginCredentialWithPhone(phone: string, otpCode: string, userId: string) {
|
|
const isValid = await this.otpService.verifyOtp(phone, otpCode, "LOGIN");
|
|
if (!isValid) throw new BadRequestException(AuthMessage.INVALID_OTP);
|
|
|
|
await this.otpService.delOtpFormCache(phone, "LOGIN");
|
|
|
|
const user = await this.userRepository.findOneBy({ id: userId });
|
|
if (!user) throw new BadRequestException(UserMessage.USER_NOT_FOUND);
|
|
|
|
return user;
|
|
}
|
|
|
|
/************************************************************ */
|
|
}
|