mirror of
https://github.com/SyncrowIOT/backend.git
synced 2025-07-15 02:15:21 +00:00
Add endpoint to disable user invitation
This commit is contained in:
@ -766,6 +766,12 @@ export class ControllerRoute {
|
|||||||
public static readonly UPDATE_USER_INVITATION_DESCRIPTION =
|
public static readonly UPDATE_USER_INVITATION_DESCRIPTION =
|
||||||
'This endpoint updates an invitation for a user to assign to role and spaces.';
|
'This endpoint updates an invitation for a user to assign to role and spaces.';
|
||||||
|
|
||||||
|
public static readonly DISABLE_USER_INVITATION_SUMMARY =
|
||||||
|
'Disable user invitation';
|
||||||
|
|
||||||
|
public static readonly DISABLE_USER_INVITATION_DESCRIPTION =
|
||||||
|
'This endpoint disables an invitation for a user to assign to role and spaces.';
|
||||||
|
|
||||||
public static readonly ACTIVATION_CODE_SUMMARY =
|
public static readonly ACTIVATION_CODE_SUMMARY =
|
||||||
'Activate Invitation Code';
|
'Activate Invitation Code';
|
||||||
|
|
||||||
|
@ -17,7 +17,10 @@ import { Permissions } from 'src/decorators/permissions.decorator';
|
|||||||
import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard';
|
import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard';
|
||||||
import { CheckEmailDto } from '../dtos/check-email.dto';
|
import { CheckEmailDto } from '../dtos/check-email.dto';
|
||||||
import { ActivateCodeDto } from '../dtos/active-code.dto';
|
import { ActivateCodeDto } from '../dtos/active-code.dto';
|
||||||
import { UpdateUserInvitationDto } from '../dtos/update.invite-user.dto';
|
import {
|
||||||
|
DisableUserInvitationDto,
|
||||||
|
UpdateUserInvitationDto,
|
||||||
|
} from '../dtos/update.invite-user.dto';
|
||||||
|
|
||||||
@ApiTags('Invite User Module')
|
@ApiTags('Invite User Module')
|
||||||
@Controller({
|
@Controller({
|
||||||
@ -93,4 +96,22 @@ export class InviteUserController {
|
|||||||
invitedUserUuid,
|
invitedUserUuid,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@UseGuards(JwtAuthGuard)
|
||||||
|
@Put(':invitedUserUuid/disable')
|
||||||
|
@ApiOperation({
|
||||||
|
summary:
|
||||||
|
ControllerRoute.INVITE_USER.ACTIONS.DISABLE_USER_INVITATION_SUMMARY,
|
||||||
|
description:
|
||||||
|
ControllerRoute.INVITE_USER.ACTIONS.DISABLE_USER_INVITATION_DESCRIPTION,
|
||||||
|
})
|
||||||
|
async disableUserInvitation(
|
||||||
|
@Param('invitedUserUuid') invitedUserUuid: string,
|
||||||
|
@Body() disableUserInvitationDto: DisableUserInvitationDto,
|
||||||
|
): Promise<BaseResponseDto> {
|
||||||
|
return await this.inviteUserService.disableUserInvitation(
|
||||||
|
disableUserInvitationDto,
|
||||||
|
invitedUserUuid,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import { ApiProperty } from '@nestjs/swagger';
|
|||||||
import {
|
import {
|
||||||
ArrayMinSize,
|
ArrayMinSize,
|
||||||
IsArray,
|
IsArray,
|
||||||
|
IsBoolean,
|
||||||
IsNotEmpty,
|
IsNotEmpty,
|
||||||
IsOptional,
|
IsOptional,
|
||||||
IsString,
|
IsString,
|
||||||
@ -72,3 +73,24 @@ export class UpdateUserInvitationDto {
|
|||||||
Object.assign(this, dto);
|
Object.assign(this, dto);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export class DisableUserInvitationDto {
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'The disable status of the user',
|
||||||
|
example: 'true',
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
@IsBoolean()
|
||||||
|
@IsNotEmpty()
|
||||||
|
public disable: boolean;
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'The project uuid of the user',
|
||||||
|
example: 'd290f1ee-6c54-4b01-90e6-d701748f0851',
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
public projectUuid: string;
|
||||||
|
constructor(dto: Partial<UpdateUserInvitationDto>) {
|
||||||
|
Object.assign(this, dto);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -24,7 +24,10 @@ import { SpaceEntity, SpaceRepository } from '@app/common/modules/space';
|
|||||||
import { ActivateCodeDto } from '../dtos/active-code.dto';
|
import { ActivateCodeDto } from '../dtos/active-code.dto';
|
||||||
import { UserSpaceService } from 'src/users/services';
|
import { UserSpaceService } from 'src/users/services';
|
||||||
import { SpaceUserService } from 'src/space/services';
|
import { SpaceUserService } from 'src/space/services';
|
||||||
import { UpdateUserInvitationDto } from '../dtos/update.invite-user.dto';
|
import {
|
||||||
|
DisableUserInvitationDto,
|
||||||
|
UpdateUserInvitationDto,
|
||||||
|
} from '../dtos/update.invite-user.dto';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class InviteUserService {
|
export class InviteUserService {
|
||||||
@ -285,10 +288,7 @@ export class InviteUserService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Perform update actions if status is 'INVITED'
|
// Perform update actions if status is 'INVITED'
|
||||||
if (
|
if (userOldData.status === UserStatusEnum.INVITED) {
|
||||||
userOldData.status === UserStatusEnum.INVITED ||
|
|
||||||
userOldData.status === UserStatusEnum.DISABLED
|
|
||||||
) {
|
|
||||||
await this.updateWhenUserIsInviteOrDisable(
|
await this.updateWhenUserIsInviteOrDisable(
|
||||||
queryRunner,
|
queryRunner,
|
||||||
dto,
|
dto,
|
||||||
@ -475,4 +475,139 @@ export class InviteUserService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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 user = await this.userRepository.findOne({
|
||||||
|
where: { inviteUser: { uuid: invitedUserUuid } },
|
||||||
|
relations: ['userSpaces.space', 'userSpaces.space.community'],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw new HttpException(
|
||||||
|
'User account not found',
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!disable) {
|
||||||
|
await this.disassociateUserFromSpaces(user, projectUuid);
|
||||||
|
await this.updateUserStatus(invitedUserUuid, projectUuid, disable);
|
||||||
|
} else if (disable) {
|
||||||
|
await this.associateUserToSpaces(
|
||||||
|
user,
|
||||||
|
userData,
|
||||||
|
projectUuid,
|
||||||
|
invitedUserUuid,
|
||||||
|
disable,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new HttpException(
|
||||||
|
'Invalid user status or action',
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async updateUserStatus(
|
||||||
|
invitedUserUuid: string,
|
||||||
|
projectUuid: string,
|
||||||
|
disable: boolean,
|
||||||
|
) {
|
||||||
|
await this.inviteUserRepository.update(
|
||||||
|
{ uuid: invitedUserUuid, project: { uuid: projectUuid } },
|
||||||
|
{ isEnabled: disable },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user