mirror of
https://github.com/SyncrowIOT/backend.git
synced 2025-08-26 03:39:40 +00:00

* fix: commission device API * task: add create booking API * add get All api for dashboard & mobile * add Find APIs for bookings * implement sending email updates on update bookable space * move email interfaces to separate files
905 lines
26 KiB
TypeScript
905 lines
26 KiB
TypeScript
import { RoleType } from '@app/common/constants/role.type.enum';
|
|
import { UserStatusEnum } from '@app/common/constants/user-status.enum';
|
|
import { BaseResponseDto } from '@app/common/dto/base.response.dto';
|
|
import { SuccessResponseDto } from '@app/common/dto/success.response.dto';
|
|
import { generateRandomString } from '@app/common/helper/randomString';
|
|
import { InviteUserEntity } from '@app/common/modules/Invite-user/entities';
|
|
import {
|
|
InviteUserRepository,
|
|
InviteUserSpaceRepository,
|
|
} from '@app/common/modules/Invite-user/repositiories';
|
|
import { ProjectEntity } from '@app/common/modules/project/entities';
|
|
import { RoleTypeEntity } from '@app/common/modules/role-type/entities';
|
|
import { RoleTypeRepository } from '@app/common/modules/role-type/repositories';
|
|
import { SpaceRepository } from '@app/common/modules/space';
|
|
import { SpaceEntity } from '@app/common/modules/space/entities/space.entity';
|
|
import { UserEntity } from '@app/common/modules/user/entities';
|
|
import { UserRepository } from '@app/common/modules/user/repositories';
|
|
import { EmailService } from '@app/common/util/email/email.service';
|
|
import {
|
|
BadRequestException,
|
|
HttpException,
|
|
HttpStatus,
|
|
Injectable,
|
|
} from '@nestjs/common';
|
|
import { SpaceUserService } from 'src/space/services';
|
|
import { UserSpaceService } from 'src/users/services';
|
|
import {
|
|
DataSource,
|
|
EntityManager,
|
|
In,
|
|
IsNull,
|
|
Not,
|
|
QueryRunner,
|
|
} from 'typeorm';
|
|
import { AddUserInvitationDto } from '../dtos';
|
|
import { ActivateCodeDto } from '../dtos/active-code.dto';
|
|
import { CheckEmailDto } from '../dtos/check-email.dto';
|
|
import {
|
|
DisableUserInvitationDto,
|
|
UpdateUserInvitationDto,
|
|
} from '../dtos/update.invite-user.dto';
|
|
|
|
@Injectable()
|
|
export class InviteUserService {
|
|
constructor(
|
|
private readonly inviteUserRepository: InviteUserRepository,
|
|
private readonly userRepository: UserRepository,
|
|
private readonly emailService: EmailService,
|
|
private readonly inviteUserSpaceRepository: InviteUserSpaceRepository,
|
|
private readonly userSpaceService: UserSpaceService,
|
|
private readonly spaceUserService: SpaceUserService,
|
|
private readonly spaceRepository: SpaceRepository,
|
|
private readonly roleTypeRepository: RoleTypeRepository,
|
|
private readonly dataSource: DataSource,
|
|
) {}
|
|
|
|
async createUserInvitation(
|
|
dto: AddUserInvitationDto,
|
|
roleType: RoleType,
|
|
): Promise<BaseResponseDto> {
|
|
const {
|
|
firstName,
|
|
lastName,
|
|
email,
|
|
jobTitle,
|
|
companyName,
|
|
phoneNumber,
|
|
roleUuid,
|
|
spaceUuids,
|
|
projectUuid,
|
|
} = dto;
|
|
|
|
const invitationCode = generateRandomString(6);
|
|
const queryRunner = this.dataSource.createQueryRunner();
|
|
|
|
await queryRunner.startTransaction();
|
|
|
|
try {
|
|
const userRepo = queryRunner.manager.getRepository(UserEntity);
|
|
await this.checkEmailAndProject({ email });
|
|
|
|
const existingUser = await userRepo.findOne({
|
|
where: {
|
|
email,
|
|
project: Not(IsNull()),
|
|
},
|
|
});
|
|
|
|
if (existingUser) {
|
|
throw new HttpException(
|
|
'User already has a project',
|
|
HttpStatus.BAD_REQUEST,
|
|
);
|
|
}
|
|
|
|
await this.checkRole(roleUuid, queryRunner);
|
|
await this.checkProject(projectUuid, queryRunner);
|
|
// Validate spaces
|
|
const validSpaces = await this.validateSpaces(
|
|
spaceUuids,
|
|
queryRunner.manager,
|
|
);
|
|
|
|
// Create invitation
|
|
const inviteUser = this.inviteUserRepository.create({
|
|
firstName,
|
|
lastName,
|
|
email,
|
|
jobTitle,
|
|
companyName,
|
|
phoneNumber,
|
|
roleType: { uuid: roleUuid },
|
|
status: UserStatusEnum.INVITED,
|
|
invitationCode,
|
|
invitedBy: roleType,
|
|
project: { uuid: projectUuid },
|
|
});
|
|
|
|
const invitedUser = await queryRunner.manager.save(inviteUser);
|
|
const invitedRoleType = await this.getRoleTypeByUuid(roleUuid);
|
|
|
|
// Link user to spaces
|
|
const spacePromises = validSpaces.map(async (space) => {
|
|
const inviteUserSpace = this.inviteUserSpaceRepository.create({
|
|
inviteUser: { uuid: invitedUser.uuid },
|
|
space: { uuid: space.uuid },
|
|
});
|
|
return queryRunner.manager.save(inviteUserSpace);
|
|
});
|
|
|
|
await Promise.all(spacePromises);
|
|
|
|
// Send invitation email
|
|
const spaceNames = validSpaces.map((space) => space.spaceName).join(', ');
|
|
await this.emailService.sendEmailWithInvitationTemplate(email, {
|
|
name: firstName,
|
|
invitationCode,
|
|
role: invitedRoleType.replace(/_/g, ' '),
|
|
spacesList: spaceNames,
|
|
});
|
|
|
|
await queryRunner.commitTransaction();
|
|
|
|
return new SuccessResponseDto({
|
|
statusCode: HttpStatus.CREATED,
|
|
success: true,
|
|
data: {
|
|
invitationCode: invitedUser.invitationCode,
|
|
},
|
|
message: 'User invited successfully',
|
|
});
|
|
} catch (error) {
|
|
await queryRunner.rollbackTransaction();
|
|
if (error instanceof HttpException) {
|
|
throw error;
|
|
}
|
|
console.error('Error creating user invitation:', error);
|
|
throw new HttpException(
|
|
error.message || 'An unexpected error occurred while inviting the user',
|
|
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
|
);
|
|
} finally {
|
|
await queryRunner.release();
|
|
}
|
|
}
|
|
|
|
async updateUserInvitation(
|
|
dto: UpdateUserInvitationDto,
|
|
invitedUserUuid: string,
|
|
): Promise<BaseResponseDto> {
|
|
const { projectUuid, spaceUuids } = dto;
|
|
|
|
const queryRunner = this.dataSource.createQueryRunner();
|
|
await queryRunner.startTransaction();
|
|
|
|
try {
|
|
// Fetch the user's existing data in the project
|
|
const userOldData = await this.inviteUserRepository.findOne({
|
|
where: { uuid: invitedUserUuid, project: { uuid: projectUuid } },
|
|
relations: ['project', 'spaces.space', 'roleType'],
|
|
});
|
|
|
|
if (!userOldData) {
|
|
throw new HttpException('User not found', HttpStatus.NOT_FOUND);
|
|
}
|
|
|
|
await this.checkRole(dto.roleUuid, queryRunner);
|
|
await this.checkProject(projectUuid, queryRunner);
|
|
|
|
// Perform update actions if status is 'INVITED'
|
|
if (userOldData.status === UserStatusEnum.INVITED) {
|
|
await this.updateWhenUserIsInvite(queryRunner, dto, invitedUserUuid);
|
|
} else if (userOldData.status === UserStatusEnum.ACTIVE) {
|
|
await this.updateWhenUserIsInvite(queryRunner, dto, invitedUserUuid);
|
|
await this.updateWhenUserIsActive(queryRunner, dto, invitedUserUuid);
|
|
}
|
|
// Extract existing space UUIDs
|
|
const oldSpaceUuids = userOldData.spaces.map((space) => space.space.uuid);
|
|
|
|
// Compare spaces
|
|
const addedSpaces = spaceUuids.filter(
|
|
(uuid) => !oldSpaceUuids.includes(uuid),
|
|
);
|
|
const removedSpaces = oldSpaceUuids.filter(
|
|
(uuid) => !spaceUuids.includes(uuid),
|
|
);
|
|
|
|
// Fetch the space names for added and removed spaces
|
|
const spaceRepo = queryRunner.manager.getRepository(SpaceEntity);
|
|
const addedSpacesDetails = await spaceRepo.find({
|
|
where: {
|
|
uuid: In(addedSpaces),
|
|
},
|
|
});
|
|
|
|
const removedSpacesDetails = await spaceRepo.find({
|
|
where: {
|
|
uuid: In(removedSpaces),
|
|
},
|
|
});
|
|
|
|
// Extract the names of the added and removed spaces
|
|
const addedSpaceNames = addedSpacesDetails.map(
|
|
(space) => space.spaceName,
|
|
);
|
|
const removedSpaceNames = removedSpacesDetails.map(
|
|
(space) => space.spaceName,
|
|
);
|
|
|
|
// Check for role and name change
|
|
const oldRole = userOldData.roleType.type;
|
|
const newRole = await this.getRoleTypeByUuid(dto.roleUuid);
|
|
const oldFullName = `${userOldData.firstName} ${userOldData.lastName}`;
|
|
const newFullName = `${dto.firstName} ${dto.lastName}`;
|
|
|
|
// Generate email body
|
|
const emailMessage = this.emailService.generateUserChangesEmailBody(
|
|
addedSpaceNames,
|
|
removedSpaceNames,
|
|
oldRole,
|
|
newRole,
|
|
oldFullName,
|
|
newFullName,
|
|
);
|
|
await this.emailService.sendEditUserEmailWithTemplate(userOldData.email, {
|
|
name: dto.firstName || userOldData.firstName,
|
|
...emailMessage,
|
|
});
|
|
|
|
// Proceed with other updates (e.g., roles, names, etc.)
|
|
await queryRunner.commitTransaction();
|
|
|
|
return new SuccessResponseDto({
|
|
statusCode: HttpStatus.OK,
|
|
success: true,
|
|
message: 'User invitation updated successfully',
|
|
});
|
|
} catch (error) {
|
|
await queryRunner.rollbackTransaction();
|
|
|
|
// Throw an appropriate HTTP exception
|
|
throw error instanceof HttpException
|
|
? error
|
|
: new HttpException(
|
|
'An unexpected error occurred while updating the user',
|
|
HttpStatus.INTERNAL_SERVER_ERROR,
|
|
);
|
|
} finally {
|
|
await queryRunner.release();
|
|
}
|
|
}
|
|
|
|
async disableUserInvitation(
|
|
dto: DisableUserInvitationDto,
|
|
invitedUserUuid: string,
|
|
): Promise<BaseResponseDto> {
|
|
const { disable, projectUuid } = dto;
|
|
|
|
const queryRunner = this.dataSource.createQueryRunner();
|
|
await queryRunner.startTransaction();
|
|
|
|
try {
|
|
const userData = await this.inviteUserRepository.findOne({
|
|
where: { uuid: invitedUserUuid, project: { uuid: projectUuid } },
|
|
relations: ['roleType', 'spaces.space'],
|
|
});
|
|
|
|
if (!userData) {
|
|
throw new HttpException('User not found', HttpStatus.NOT_FOUND);
|
|
}
|
|
|
|
if (userData.status === UserStatusEnum.INVITED) {
|
|
await this.updateUserStatus(invitedUserUuid, projectUuid, !disable);
|
|
} else if (userData.status === UserStatusEnum.ACTIVE) {
|
|
const invitedUserData = await this.inviteUserRepository.findOne({
|
|
where: { uuid: invitedUserUuid },
|
|
relations: [
|
|
'user.userSpaces.space',
|
|
'user.userSpaces.space.community',
|
|
],
|
|
});
|
|
|
|
if (!invitedUserData.user) {
|
|
throw new HttpException(
|
|
'User account not found',
|
|
HttpStatus.NOT_FOUND,
|
|
);
|
|
}
|
|
|
|
if (disable) {
|
|
await this.disassociateUserFromSpaces(
|
|
invitedUserData.user,
|
|
projectUuid,
|
|
);
|
|
await this.updateUserStatus(invitedUserUuid, projectUuid, !disable);
|
|
} else if (!disable) {
|
|
await this.associateUserToSpaces(
|
|
invitedUserData.user,
|
|
userData,
|
|
projectUuid,
|
|
invitedUserUuid,
|
|
!disable,
|
|
);
|
|
}
|
|
} else {
|
|
throw new HttpException(
|
|
'Invalid user status or action',
|
|
HttpStatus.BAD_REQUEST,
|
|
);
|
|
}
|
|
await this.emailService.sendEmailWithTemplate({
|
|
email: userData.email,
|
|
name: userData.firstName,
|
|
isEnable: !disable,
|
|
isDelete: false,
|
|
});
|
|
await queryRunner.commitTransaction();
|
|
|
|
return new SuccessResponseDto({
|
|
statusCode: HttpStatus.OK,
|
|
success: true,
|
|
message: 'User invitation status updated successfully',
|
|
});
|
|
} catch (error) {
|
|
await queryRunner.rollbackTransaction();
|
|
throw error;
|
|
} finally {
|
|
await queryRunner.release();
|
|
}
|
|
}
|
|
|
|
async deleteUserInvitation(
|
|
invitedUserUuid: string,
|
|
): Promise<BaseResponseDto> {
|
|
const queryRunner = this.dataSource.createQueryRunner();
|
|
await queryRunner.startTransaction();
|
|
|
|
try {
|
|
const userData = await this.inviteUserRepository.findOne({
|
|
where: { uuid: invitedUserUuid },
|
|
relations: ['roleType', 'spaces.space', 'project'],
|
|
});
|
|
|
|
if (!userData) {
|
|
throw new HttpException('User not found', HttpStatus.NOT_FOUND);
|
|
}
|
|
|
|
if (userData.status === UserStatusEnum.INVITED) {
|
|
await this.inviteUserRepository.update(
|
|
{ uuid: invitedUserUuid },
|
|
{ isActive: false },
|
|
);
|
|
} else if (userData.status === UserStatusEnum.ACTIVE) {
|
|
const invitedUserData = await this.inviteUserRepository.findOne({
|
|
where: { uuid: invitedUserUuid },
|
|
relations: [
|
|
'user.userSpaces.space',
|
|
'user.userSpaces.space.community',
|
|
],
|
|
});
|
|
|
|
if (!invitedUserData.user) {
|
|
throw new HttpException(
|
|
'User account not found',
|
|
HttpStatus.NOT_FOUND,
|
|
);
|
|
}
|
|
|
|
await this.disassociateUserFromSpaces(
|
|
invitedUserData.user,
|
|
userData.project.uuid,
|
|
);
|
|
await this.inviteUserRepository.update(
|
|
{ uuid: invitedUserUuid },
|
|
{ isActive: false },
|
|
);
|
|
await this.userRepository.update(
|
|
{ uuid: invitedUserData.user.uuid },
|
|
{ isActive: false },
|
|
);
|
|
}
|
|
await this.emailService.sendEmailWithTemplate({
|
|
email: userData.email,
|
|
name: userData.firstName,
|
|
isEnable: false,
|
|
isDelete: true,
|
|
});
|
|
await queryRunner.commitTransaction();
|
|
|
|
return new SuccessResponseDto({
|
|
statusCode: HttpStatus.OK,
|
|
success: true,
|
|
message: 'User invitation deleted successfully',
|
|
});
|
|
} catch (error) {
|
|
await queryRunner.rollbackTransaction();
|
|
throw error;
|
|
} finally {
|
|
await queryRunner.release();
|
|
}
|
|
}
|
|
|
|
async activationCode(dto: ActivateCodeDto): Promise<BaseResponseDto> {
|
|
const { activationCode, userUuid } = dto;
|
|
|
|
try {
|
|
const user = await this.getUser(userUuid);
|
|
|
|
const invitedUser = await this.inviteUserRepository.findOne({
|
|
where: {
|
|
email: user.email,
|
|
status: UserStatusEnum.INVITED,
|
|
isActive: true,
|
|
},
|
|
relations: ['project', 'spaces.space.community', 'roleType'],
|
|
});
|
|
|
|
if (invitedUser) {
|
|
if (invitedUser.invitationCode !== activationCode) {
|
|
throw new HttpException(
|
|
'Invalid activation code',
|
|
HttpStatus.BAD_REQUEST,
|
|
);
|
|
}
|
|
|
|
// Handle invited user with valid activation code
|
|
await this.handleInvitedUser(user, invitedUser);
|
|
} else {
|
|
// Handle case for non-invited user
|
|
await this.handleNonInvitedUser(activationCode, userUuid);
|
|
}
|
|
return new SuccessResponseDto({
|
|
statusCode: HttpStatus.OK,
|
|
success: true,
|
|
message: 'The code has been successfully activated',
|
|
});
|
|
} catch (error) {
|
|
console.error('Error activating the code:', error);
|
|
throw new HttpException(
|
|
error.message ||
|
|
'An unexpected error occurred while activating the code',
|
|
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
|
);
|
|
}
|
|
}
|
|
|
|
async checkEmailAndProject(dto: CheckEmailDto): Promise<BaseResponseDto> {
|
|
const { email } = dto;
|
|
|
|
try {
|
|
const user = await this.userRepository.findOne({
|
|
where: { email },
|
|
relations: ['project'],
|
|
});
|
|
this.validateUserOrInvite(user, 'User');
|
|
const invitedUser = await this.inviteUserRepository.findOne({
|
|
where: { email },
|
|
relations: ['project'],
|
|
});
|
|
this.validateUserOrInvite(invitedUser, 'Invited User');
|
|
|
|
return new SuccessResponseDto({
|
|
statusCode: HttpStatus.OK,
|
|
success: true,
|
|
message: 'Valid email',
|
|
});
|
|
} catch (error) {
|
|
console.error('Error checking email and project:', error);
|
|
throw new HttpException(
|
|
error.message ||
|
|
'An unexpected error occurred while checking the email',
|
|
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
|
);
|
|
}
|
|
}
|
|
|
|
private async getSpaceByUuid(spaceUuid: string) {
|
|
try {
|
|
const space = await this.spaceRepository.findOne({
|
|
where: {
|
|
uuid: spaceUuid,
|
|
},
|
|
relations: ['community'],
|
|
});
|
|
if (!space) {
|
|
throw new BadRequestException(`Invalid space UUID ${spaceUuid}`);
|
|
}
|
|
return {
|
|
uuid: space.uuid,
|
|
createdAt: space.createdAt,
|
|
updatedAt: space.updatedAt,
|
|
name: space.spaceName,
|
|
spaceTuyaUuid: space.community.externalId,
|
|
communityUuid: space.community.uuid,
|
|
};
|
|
} catch (err) {
|
|
if (err instanceof BadRequestException) {
|
|
throw err; // Re-throw BadRequestException
|
|
} else {
|
|
throw new HttpException('Space not found', HttpStatus.NOT_FOUND);
|
|
}
|
|
}
|
|
}
|
|
|
|
private async validateSpaces(
|
|
spaceUuids: string[],
|
|
entityManager: EntityManager,
|
|
): Promise<SpaceEntity[]> {
|
|
const spaceRepo = entityManager.getRepository(SpaceEntity);
|
|
|
|
const validSpaces = await spaceRepo.find({
|
|
where: { uuid: In(spaceUuids) },
|
|
});
|
|
|
|
if (validSpaces.length !== spaceUuids.length) {
|
|
const validSpaceUuids = validSpaces.map((space) => space.uuid);
|
|
const invalidSpaceUuids = spaceUuids.filter(
|
|
(uuid) => !validSpaceUuids.includes(uuid),
|
|
);
|
|
throw new HttpException(
|
|
`Invalid space UUIDs: ${invalidSpaceUuids.join(', ')}`,
|
|
HttpStatus.BAD_REQUEST,
|
|
);
|
|
}
|
|
|
|
return validSpaces;
|
|
}
|
|
|
|
private async checkRole(
|
|
roleUuid: string,
|
|
queryRunner: QueryRunner,
|
|
): Promise<BaseResponseDto> {
|
|
try {
|
|
const role = await queryRunner.manager.findOne(RoleTypeEntity, {
|
|
where: { uuid: roleUuid },
|
|
});
|
|
|
|
if (!role) {
|
|
throw new HttpException('Role not found', HttpStatus.NOT_FOUND);
|
|
}
|
|
|
|
return new SuccessResponseDto({
|
|
statusCode: HttpStatus.OK,
|
|
success: true,
|
|
message: 'Valid role',
|
|
});
|
|
} catch (error) {
|
|
console.error('Error checking role:', error);
|
|
throw new HttpException(
|
|
error.message || 'An unexpected error occurred while checking the role',
|
|
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
|
);
|
|
}
|
|
}
|
|
|
|
private async checkProject(
|
|
projectUuid: string,
|
|
queryRunner: QueryRunner,
|
|
): Promise<BaseResponseDto> {
|
|
try {
|
|
const project = await queryRunner.manager.findOne(ProjectEntity, {
|
|
where: { uuid: projectUuid },
|
|
});
|
|
|
|
if (!project) {
|
|
throw new HttpException('Project not found', HttpStatus.NOT_FOUND);
|
|
}
|
|
|
|
return new SuccessResponseDto({
|
|
statusCode: HttpStatus.OK,
|
|
success: true,
|
|
message: 'Valid project',
|
|
});
|
|
} catch (error) {
|
|
console.error('Error checking project:', error);
|
|
throw new HttpException(
|
|
error.message ||
|
|
'An unexpected error occurred while checking the project',
|
|
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
|
);
|
|
}
|
|
}
|
|
|
|
private validateUserOrInvite(user: any, userType: string): void {
|
|
if (user) {
|
|
if (!user.isActive) {
|
|
throw new HttpException(
|
|
`${userType} is deleted`,
|
|
HttpStatus.BAD_REQUEST,
|
|
);
|
|
}
|
|
if (user.project) {
|
|
throw new HttpException(
|
|
`${userType} already has a project`,
|
|
HttpStatus.BAD_REQUEST,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
private async getUser(userUuid: string): Promise<UserEntity> {
|
|
const user = await this.userRepository.findOne({
|
|
where: { uuid: userUuid, isActive: true, isUserVerified: true },
|
|
});
|
|
|
|
if (!user) {
|
|
throw new HttpException('User not found', HttpStatus.NOT_FOUND);
|
|
}
|
|
return user;
|
|
}
|
|
|
|
private async handleNonInvitedUser(
|
|
activationCode: string,
|
|
userUuid: string,
|
|
): Promise<void> {
|
|
await this.userSpaceService.verifyCodeAndAddUserSpace(
|
|
{ inviteCode: activationCode },
|
|
userUuid,
|
|
);
|
|
}
|
|
|
|
private async handleInvitedUser(
|
|
user: UserEntity,
|
|
invitedUser: InviteUserEntity,
|
|
): Promise<void> {
|
|
for (const invitedSpace of invitedUser.spaces) {
|
|
try {
|
|
const deviceUUIDs = await this.userSpaceService.getDeviceUUIDsForSpace(
|
|
invitedSpace.space.uuid,
|
|
);
|
|
|
|
await this.userSpaceService.addUserPermissionsToDevices(
|
|
user.uuid,
|
|
deviceUUIDs,
|
|
);
|
|
|
|
await this.spaceUserService.associateUserToSpace({
|
|
communityUuid: invitedSpace.space.community.uuid,
|
|
spaceUuid: invitedSpace.space.uuid,
|
|
userUuid: user.uuid,
|
|
projectUuid: invitedUser.project.uuid,
|
|
});
|
|
} catch (spaceError) {
|
|
console.error(
|
|
`Error processing space ${invitedSpace.space.uuid}:`,
|
|
spaceError,
|
|
);
|
|
continue; // Skip to the next space
|
|
}
|
|
}
|
|
|
|
// Update invited user and associated user data
|
|
await this.inviteUserRepository.update(
|
|
{ uuid: invitedUser.uuid },
|
|
{ status: UserStatusEnum.ACTIVE, user: { uuid: user.uuid } },
|
|
);
|
|
await this.userRepository.update(
|
|
{ uuid: user.uuid },
|
|
{
|
|
project: { uuid: invitedUser.project.uuid },
|
|
roleType: { uuid: invitedUser.roleType.uuid },
|
|
},
|
|
);
|
|
}
|
|
|
|
private async getRoleTypeByUuid(roleUuid: string) {
|
|
const role = await this.roleTypeRepository.findOne({
|
|
where: { uuid: roleUuid },
|
|
});
|
|
|
|
return role.type;
|
|
}
|
|
|
|
private async updateWhenUserIsInvite(
|
|
queryRunner: QueryRunner,
|
|
dto: UpdateUserInvitationDto,
|
|
invitedUserUuid: string,
|
|
): Promise<void> {
|
|
const {
|
|
firstName,
|
|
lastName,
|
|
jobTitle,
|
|
companyName,
|
|
phoneNumber,
|
|
roleUuid,
|
|
spaceUuids,
|
|
} = dto;
|
|
|
|
// Update user invitation details
|
|
await queryRunner.manager.update(
|
|
this.inviteUserRepository.target,
|
|
{ uuid: invitedUserUuid },
|
|
{
|
|
firstName,
|
|
lastName,
|
|
jobTitle,
|
|
companyName,
|
|
phoneNumber,
|
|
roleType: { uuid: roleUuid },
|
|
},
|
|
);
|
|
|
|
// Remove old space associations
|
|
await queryRunner.manager.delete(this.inviteUserSpaceRepository.target, {
|
|
inviteUser: { uuid: invitedUserUuid },
|
|
});
|
|
|
|
// Save new space associations
|
|
const spaceData = spaceUuids.map((spaceUuid) => ({
|
|
inviteUser: { uuid: invitedUserUuid },
|
|
space: { uuid: spaceUuid },
|
|
}));
|
|
await queryRunner.manager.save(
|
|
this.inviteUserSpaceRepository.target,
|
|
spaceData,
|
|
);
|
|
}
|
|
private async updateWhenUserIsActive(
|
|
queryRunner: QueryRunner,
|
|
dto: UpdateUserInvitationDto,
|
|
invitedUserUuid: string,
|
|
): Promise<void> {
|
|
const {
|
|
firstName,
|
|
lastName,
|
|
jobTitle,
|
|
companyName,
|
|
phoneNumber,
|
|
roleUuid,
|
|
spaceUuids,
|
|
projectUuid,
|
|
} = dto;
|
|
|
|
const userData = await this.inviteUserRepository.findOne({
|
|
where: { uuid: invitedUserUuid },
|
|
relations: ['user.userSpaces.space', 'user.userSpaces.space.community'],
|
|
});
|
|
|
|
if (!userData) {
|
|
throw new HttpException(
|
|
`User with invitedUserUuid ${invitedUserUuid} not found`,
|
|
HttpStatus.NOT_FOUND,
|
|
);
|
|
}
|
|
|
|
// Update user details
|
|
await queryRunner.manager.update(
|
|
this.inviteUserRepository.target,
|
|
{ uuid: invitedUserUuid },
|
|
{
|
|
firstName,
|
|
lastName,
|
|
jobTitle,
|
|
companyName,
|
|
phoneNumber,
|
|
roleType: { uuid: roleUuid },
|
|
},
|
|
);
|
|
await this.userRepository.update(
|
|
{ uuid: userData.user.uuid },
|
|
{
|
|
roleType: { uuid: roleUuid },
|
|
},
|
|
);
|
|
// Disassociate the user from all current spaces
|
|
const disassociatePromises = userData.user.userSpaces.map((userSpace) =>
|
|
this.spaceUserService
|
|
.disassociateUserFromSpace({
|
|
communityUuid: userSpace.space.community.uuid,
|
|
spaceUuid: userSpace.space.uuid,
|
|
userUuid: userData.user.uuid,
|
|
projectUuid,
|
|
})
|
|
.catch((error) => {
|
|
console.error(
|
|
`Failed to disassociate user from space ${userSpace.space.uuid}:`,
|
|
error,
|
|
);
|
|
throw error;
|
|
}),
|
|
);
|
|
|
|
await Promise.allSettled(disassociatePromises);
|
|
|
|
// Process new spaces
|
|
const associatePromises = spaceUuids.map(async (spaceUuid) => {
|
|
try {
|
|
// Fetch space details
|
|
const spaceDetails = await this.getSpaceByUuid(spaceUuid);
|
|
|
|
// Fetch device UUIDs for the space
|
|
const deviceUUIDs =
|
|
await this.userSpaceService.getDeviceUUIDsForSpace(spaceUuid);
|
|
|
|
// Grant permissions to the user for all devices in the space
|
|
await this.userSpaceService.addUserPermissionsToDevices(
|
|
userData.user.uuid,
|
|
deviceUUIDs,
|
|
);
|
|
|
|
// Associate the user with the new space
|
|
await this.spaceUserService.associateUserToSpace({
|
|
communityUuid: spaceDetails.communityUuid,
|
|
spaceUuid: spaceUuid,
|
|
userUuid: userData.user.uuid,
|
|
projectUuid,
|
|
});
|
|
} catch (error) {
|
|
console.error(`Failed to process space ${spaceUuid}:`, error);
|
|
throw error;
|
|
}
|
|
});
|
|
|
|
await Promise.all(associatePromises);
|
|
}
|
|
|
|
private async updateUserStatus(
|
|
invitedUserUuid: string,
|
|
projectUuid: string,
|
|
isEnabled: boolean,
|
|
) {
|
|
await this.inviteUserRepository.update(
|
|
{ uuid: invitedUserUuid, project: { uuid: projectUuid } },
|
|
{ isEnabled },
|
|
);
|
|
}
|
|
|
|
private async disassociateUserFromSpaces(user: any, projectUuid: string) {
|
|
const disassociatePromises = user.userSpaces.map((userSpace) =>
|
|
this.spaceUserService.disassociateUserFromSpace({
|
|
communityUuid: userSpace.space.community.uuid,
|
|
spaceUuid: userSpace.space.uuid,
|
|
userUuid: user.uuid,
|
|
projectUuid,
|
|
}),
|
|
);
|
|
|
|
const results = await Promise.allSettled(disassociatePromises);
|
|
|
|
results.forEach((result, index) => {
|
|
if (result.status === 'rejected') {
|
|
console.error(
|
|
`Failed to disassociate user from space ${user.userSpaces[index].space.uuid}:`,
|
|
result.reason,
|
|
);
|
|
}
|
|
});
|
|
}
|
|
private async associateUserToSpaces(
|
|
user: any,
|
|
userData: any,
|
|
projectUuid: string,
|
|
invitedUserUuid: string,
|
|
disable: boolean,
|
|
) {
|
|
const spaceUuids = userData.spaces.map((space) => space.space.uuid);
|
|
|
|
const associatePromises = spaceUuids.map(async (spaceUuid) => {
|
|
try {
|
|
const spaceDetails = await this.getSpaceByUuid(spaceUuid);
|
|
|
|
const deviceUUIDs =
|
|
await this.userSpaceService.getDeviceUUIDsForSpace(spaceUuid);
|
|
await this.userSpaceService.addUserPermissionsToDevices(
|
|
user.uuid,
|
|
deviceUUIDs,
|
|
);
|
|
|
|
await this.spaceUserService.associateUserToSpace({
|
|
communityUuid: spaceDetails.communityUuid,
|
|
spaceUuid,
|
|
userUuid: user.uuid,
|
|
projectUuid,
|
|
});
|
|
|
|
await this.updateUserStatus(invitedUserUuid, projectUuid, disable);
|
|
} catch (error) {
|
|
console.error(`Failed to associate user to space ${spaceUuid}:`, error);
|
|
}
|
|
});
|
|
|
|
await Promise.allSettled(associatePromises);
|
|
}
|
|
}
|