import { RoleService } from './role.service'; import { BadRequestException, ForbiddenException, Inject, Injectable, } from '@nestjs/common'; import { EntityServiceImpl } from '../../meta/service/entity-service-impl.service'; import { UserData } from '../entity/user.entity'; import { BaseEntity } from '../../meta/entity/base-entity.entity'; import { UserRepository } from '../repository/user.repository'; import { EncryptUtilService } from '../../../utils/service/encryptUtil.service'; import { ClockIDGenService } from '../../../utils/service/clockIDGenUtil.service'; import { ENTITYTYPE_ROLE, STATUS_ACTIVE, INVITATION_STATUS_SENT, } from '../../../constant/global.constant'; import { CreateUserDto } from '../dto/create-user.dto'; import { UserRoleMappingService } from './user-role-mapping.service'; import { UserRoleMapping } from '../entity/user-role-mapping.entity'; import { EntityManager, Repository } from 'typeorm'; import { UpdateUserDto } from '../dto/update-user.dto'; import { ConfigService } from '@nestjs/config'; // import { UserAppMappingService } from 'src/module/meta/service/user-app-mapping.service'; import { ServiceResult } from 'src/dtos/response.dto'; import { ListMasterService } from 'src/module/listmaster/service/list-master.service'; import { OrganizationRepository } from 'src/module/enterprise/repository/organization.repository'; import { InjectRepository } from '@nestjs/typeorm'; import { Action } from 'src/module/workflow-automation/interface/action.interface'; import { ActionHandler } from 'src/module/workflow-automation/interface/action.decorator'; @Injectable() @ActionHandler('User') export class UserService extends EntityServiceImpl implements Action { constructor( private userRepository: UserRepository, private userRoleMappingService: UserRoleMappingService, private readonly clockIDGenService: ClockIDGenService, private configService: ConfigService, private readonly organizationRepository: OrganizationRepository, // private readonly userAppMappingService: UserAppMappingService, @Inject('RoleService') private readonly roleService: RoleService, @Inject('ListMasterService') private readonly listMasterService: ListMasterService, ) { super(); } masterKey = this.configService.get('MASTER_KEY') || ''; masterIv = this.configService.get('MASTER_IV') || ''; async createEntity( entityData: BaseEntity, loggedInUser: UserData | null, manager?: EntityManager, ): Promise> { const userData = entityData as CreateUserDto; let existingUser = await this.userRepository.findByEmailId( userData.email_id, loggedInUser?.organization_id, ); if (existingUser) { return { success: false, error: 'User with this email already exists' }; } existingUser = await this.userRepository.findByMobile( userData.mobile, loggedInUser?.organization_id, ); if (existingUser) { return { success: false, error: 'User with this mobile already exists' }; } const resolveStatus = await this.listMasterService.getResolvedListCode( STATUS_ACTIVE, loggedInUser?.organization_id || 0, ); const resolvedInvitationStatus = await this.listMasterService.getResolvedListCode( INVITATION_STATUS_SENT, loggedInUser?.organization_id || 0, ); userData.name = (userData.first_name || '') + ' ' + (userData.last_name || ''); userData.password = EncryptUtilService.encryptGCM( userData.password || 'Admin@123', this.masterKey, this.masterIv, ); userData.is_firstlogin = 1; userData.roles = []; userData.invitation_status = resolvedInvitationStatus.id; userData.status = resolveStatus.id || 'ACTIVE'; const savedData = await super.createEntity(userData, loggedInUser); const insertPromises: Promise[] = []; for (const entry of userData.access || []) { const { level_type, level_ids, app_code, role_id } = entry; if ( !level_type || !Array.isArray(level_ids) || !level_ids.length || !app_code || !role_id ) { return { success: false, error: 'Invalid access level entry' }; } for (const levelId of level_ids) { const userRoleMapping = new UserRoleMapping(savedData.id, role_id); userRoleMapping.level_type = level_type; userRoleMapping.level_id = String(levelId); userRoleMapping.appcode = app_code; userRoleMapping.organization_id = loggedInUser?.organization_id || 0; insertPromises.push( this.userRoleMappingService.assignUserRole(userRoleMapping), ); } } try { if (insertPromises.length > 0) { await Promise.all(insertPromises); } } catch (error) { console.error('Error adding access levels:', error); return { success: false, error: 'Failed to add access levels' }; } return { success: true, data: savedData }; } name: string = 'UserService'; async execute(payload: any): Promise { console.log('payload', payload); } async getEntityData( entityType: string, id: number, loggedInUser?: UserData, ): Promise { const user = await this.userRepository.findById(id); if (user) { const userRoleMappings: UserRoleMapping[] | null = await this.userRoleMappingService.findByUserId(id); if (userRoleMappings) { const userDto = user as unknown as CreateUserDto; const roles: any[] = []; for (const i in userRoleMappings) { const userRoleMapping = userRoleMappings[i]; const roleId = userRoleMapping.role_id; const role = await super.getEntityData( ENTITYTYPE_ROLE, roleId, loggedInUser, ); roles.push(role); } userDto.roles = roles; } return user; } return null; } async updateEntity( entityData: BaseEntity, loggedInUserData: UserData, ): Promise> { const userDto = entityData as UpdateUserDto; const existingUser = await this.userRepository.findById(entityData.id); if (!existingUser) { return { success: false, error: 'User not found' }; } if (userDto.password) { const decryptedPassword = EncryptUtilService.decryptGCM( existingUser.password, this.masterKey, this.masterIv, ); if (decryptedPassword === userDto.password) { return { success: false, error: 'New password cannot be the same as the current password', }; } userDto.password = EncryptUtilService.encryptGCM( userDto.password, this.masterKey, this.masterIv, ); } const updatedUserData = { ...userDto } as any; delete updatedUserData.access; const savedData = await super.updateEntity( updatedUserData, loggedInUserData, ); // Handle updated access levels if (userDto.access && userDto.access.length > 0) { if (loggedInUserData.level_type == 'ORG') { await this.userRoleMappingService.deleteByUserId(existingUser.id); } else { await this.userRoleMappingService.deleteByUserId( existingUser.id, loggedInUserData.level_type, loggedInUserData.level_id, ); } const insertPromises: any[] = []; for (const entry of userDto.access) { const { level_type, level_ids, app_code, role_id } = entry; if (!level_type || !app_code || !role_id || !Array.isArray(level_ids)) { return { success: false, error: 'Invalid access level entry' }; } for (const levelId of level_ids) { const userRoleMapping = new UserRoleMapping(savedData.id, role_id); userRoleMapping.level_type = level_type; userRoleMapping.level_id = String(levelId); userRoleMapping.appcode = app_code; userRoleMapping.organization_id = loggedInUserData?.organization_id || 0; insertPromises.push( this.userRoleMappingService.assignUserRole(userRoleMapping), ); } } try { await Promise.all(insertPromises); } catch (error) { console.error('Error updating access levels:', error); return { success: false, error: 'Failed to update access levels' }; } } return { success: true, data: savedData }; } async findByEmailId( email_id: string, organization_id?: number, ): Promise { return await this.userRepository.findByEmailId(email_id, organization_id); } async findByMobile( mobile: string, organization_id?: number, appCode?: string, ): Promise { return await this.userRepository.findByMobile( mobile, organization_id, appCode, ); } async setDefaultLastAccess(userId: number, appcode: string): Promise { const user = await this.userRepository.findById(userId); if (!user) { throw new BadRequestException('User not found'); } user.last_app_access = appcode; await this.userRepository.saveUser(user); // This persists the updated field } async setLastLevelTypeAndId( userId: number, levelType: string, levelId: string, appcode: string, ): Promise { const user = await this.userRepository.findById(userId); if (!user) { throw new BadRequestException('User not found'); } user.last_level_type = levelType; user.last_level_id = levelId; user.last_app_access = appcode; await this.userRepository.saveUser(user); // This persists the updated field } async checkEmailExists(data: { email_id: string; subdomain: string; }): Promise { const { email_id, subdomain } = data; if (!email_id || !subdomain) { return { success: false, message: 'Email and Subdomain is required' }; } let organization; if (subdomain) { organization = await this.organizationRepository.findOrganizationBySubdomain( subdomain, ); if (!organization) { // throw new BadRequestException('Organization not found.'); return { success: false, message: 'Organization not found.', }; } } // 🔹 Step 2: Find the user by email + organization check const user = await this.userRepository.findByEmailId( email_id, organization?.id, ); if (!user || (organization && user.organization_id !== organization.id)) { // throw new BadRequestException('User not found in organization.'); return { success: false, message: 'User not found in organization.', }; } if (user) { return { success: true, message: 'An account already exists for this email address. Login or use a different email address to sign up.', userId: user.id, }; } else { // throw new ForbiddenException('No account found with this email address.'); return { success: false, message: 'No account found with this email address.', }; } } }