mirror of
https://github.com/SyncrowIOT/backend.git
synced 2025-11-26 09:24:54 +00:00
Merge pull request #197 from SyncrowIOT/SP-904-be-disable-role-and-delete
Sp 904 be disable role and delete
This commit is contained in:
@ -58,6 +58,14 @@ MAILTRAP_API_TOKEN=
|
|||||||
|
|
||||||
MAILTRAP_INVITATION_TEMPLATE_UUID=
|
MAILTRAP_INVITATION_TEMPLATE_UUID=
|
||||||
|
|
||||||
|
MAILTRAP_EDIT_USER_TEMPLATE_UUID=
|
||||||
|
|
||||||
|
MAILTRAP_DISABLE_TEMPLATE_UUID=
|
||||||
|
|
||||||
|
MAILTRAP_ENABLE_TEMPLATE_UUID=
|
||||||
|
|
||||||
|
MAILTRAP_DELETE_USER_TEMPLATE_UUID=
|
||||||
|
|
||||||
WEBSITES_ENABLE_APP_SERVICE_STORAGE=
|
WEBSITES_ENABLE_APP_SERVICE_STORAGE=
|
||||||
|
|
||||||
PORT=
|
PORT=
|
||||||
|
|||||||
@ -45,6 +45,9 @@ export class AuthService {
|
|||||||
if (!user.isUserVerified) {
|
if (!user.isUserVerified) {
|
||||||
throw new BadRequestException('User is not verified');
|
throw new BadRequestException('User is not verified');
|
||||||
}
|
}
|
||||||
|
if (!user.isActive) {
|
||||||
|
throw new BadRequestException('User is not active');
|
||||||
|
}
|
||||||
if (user) {
|
if (user) {
|
||||||
const passwordMatch = this.helperHashService.bcryptCompare(
|
const passwordMatch = this.helperHashService.bcryptCompare(
|
||||||
pass,
|
pass,
|
||||||
|
|||||||
@ -13,5 +13,11 @@ export default registerAs(
|
|||||||
MAILTRAP_API_TOKEN: process.env.MAILTRAP_API_TOKEN,
|
MAILTRAP_API_TOKEN: process.env.MAILTRAP_API_TOKEN,
|
||||||
MAILTRAP_INVITATION_TEMPLATE_UUID:
|
MAILTRAP_INVITATION_TEMPLATE_UUID:
|
||||||
process.env.MAILTRAP_INVITATION_TEMPLATE_UUID,
|
process.env.MAILTRAP_INVITATION_TEMPLATE_UUID,
|
||||||
|
MAILTRAP_DISABLE_TEMPLATE_UUID: process.env.MAILTRAP_DISABLE_TEMPLATE_UUID,
|
||||||
|
MAILTRAP_ENABLE_TEMPLATE_UUID: process.env.MAILTRAP_ENABLE_TEMPLATE_UUID,
|
||||||
|
MAILTRAP_DELETE_USER_TEMPLATE_UUID:
|
||||||
|
process.env.MAILTRAP_DELETE_USER_TEMPLATE_UUID,
|
||||||
|
MAILTRAP_EDIT_USER_TEMPLATE_UUID:
|
||||||
|
process.env.MAILTRAP_EDIT_USER_TEMPLATE_UUID,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -766,6 +766,18 @@ 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 DELETE_USER_INVITATION_SUMMARY =
|
||||||
|
'Delete user invitation';
|
||||||
|
|
||||||
|
public static readonly DELETE_USER_INVITATION_DESCRIPTION =
|
||||||
|
'This endpoint deletes 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';
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
export enum UserStatusEnum {
|
export enum UserStatusEnum {
|
||||||
ACTIVE = 'active',
|
ACTIVE = 'active',
|
||||||
INVITED = 'invited',
|
INVITED = 'invited',
|
||||||
DISABLED = 'disabled',
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -61,6 +61,11 @@ export class InviteUserEntity extends AbstractEntity<InviteUserDto> {
|
|||||||
default: true,
|
default: true,
|
||||||
})
|
})
|
||||||
public isActive: boolean;
|
public isActive: boolean;
|
||||||
|
@Column({
|
||||||
|
nullable: false,
|
||||||
|
default: true,
|
||||||
|
})
|
||||||
|
public isEnabled: boolean;
|
||||||
@Column({
|
@Column({
|
||||||
nullable: false,
|
nullable: false,
|
||||||
unique: true,
|
unique: true,
|
||||||
|
|||||||
@ -83,4 +83,137 @@ export class EmailService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
async sendEmailWithTemplate(
|
||||||
|
email: string,
|
||||||
|
name: string,
|
||||||
|
isEnable: boolean,
|
||||||
|
isDelete: boolean,
|
||||||
|
): Promise<void> {
|
||||||
|
const isProduction = process.env.NODE_ENV === 'production';
|
||||||
|
const API_TOKEN = this.configService.get<string>(
|
||||||
|
'email-config.MAILTRAP_API_TOKEN',
|
||||||
|
);
|
||||||
|
const API_URL = isProduction
|
||||||
|
? SEND_EMAIL_API_URL_PROD
|
||||||
|
: SEND_EMAIL_API_URL_DEV;
|
||||||
|
|
||||||
|
// Determine the template UUID based on the arguments
|
||||||
|
const templateUuid = isDelete
|
||||||
|
? this.configService.get<string>(
|
||||||
|
'email-config.MAILTRAP_DELETE_USER_TEMPLATE_UUID',
|
||||||
|
)
|
||||||
|
: this.configService.get<string>(
|
||||||
|
isEnable
|
||||||
|
? 'email-config.MAILTRAP_ENABLE_TEMPLATE_UUID'
|
||||||
|
: 'email-config.MAILTRAP_DISABLE_TEMPLATE_UUID',
|
||||||
|
);
|
||||||
|
|
||||||
|
const emailData = {
|
||||||
|
from: {
|
||||||
|
email: this.smtpConfig.sender,
|
||||||
|
},
|
||||||
|
to: [
|
||||||
|
{
|
||||||
|
email,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
template_uuid: templateUuid,
|
||||||
|
template_variables: {
|
||||||
|
name,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await axios.post(API_URL, emailData, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${API_TOKEN}`,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
throw new HttpException(
|
||||||
|
error.response?.data?.message ||
|
||||||
|
'Error sending email using Mailtrap template',
|
||||||
|
error.response?.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async sendEditUserEmailWithTemplate(
|
||||||
|
email: string,
|
||||||
|
emailEditData: any,
|
||||||
|
): Promise<void> {
|
||||||
|
const isProduction = process.env.NODE_ENV === 'production';
|
||||||
|
const API_TOKEN = this.configService.get<string>(
|
||||||
|
'email-config.MAILTRAP_API_TOKEN',
|
||||||
|
);
|
||||||
|
const API_URL = isProduction
|
||||||
|
? SEND_EMAIL_API_URL_PROD
|
||||||
|
: SEND_EMAIL_API_URL_DEV;
|
||||||
|
const TEMPLATE_UUID = this.configService.get<string>(
|
||||||
|
'email-config.MAILTRAP_EDIT_USER_TEMPLATE_UUID',
|
||||||
|
);
|
||||||
|
|
||||||
|
const emailData = {
|
||||||
|
from: {
|
||||||
|
email: this.smtpConfig.sender,
|
||||||
|
},
|
||||||
|
to: [
|
||||||
|
{
|
||||||
|
email: email,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
template_uuid: TEMPLATE_UUID,
|
||||||
|
template_variables: emailEditData,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await axios.post(API_URL, emailData, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${API_TOKEN}`,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
throw new HttpException(
|
||||||
|
error.response?.data?.message ||
|
||||||
|
'Error sending email using Mailtrap template',
|
||||||
|
error.response?.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
generateUserChangesEmailBody(
|
||||||
|
addedSpaceNames: string[],
|
||||||
|
removedSpaceNames: string[],
|
||||||
|
oldRole: string,
|
||||||
|
newRole: string,
|
||||||
|
oldName: string,
|
||||||
|
newName: string,
|
||||||
|
) {
|
||||||
|
const addedSpaceNamesChanged =
|
||||||
|
addedSpaceNames.length > 0
|
||||||
|
? `Access to the following spaces were added: ${addedSpaceNames.join(', ')}`
|
||||||
|
: '';
|
||||||
|
|
||||||
|
const removedSpaceNamesChanged =
|
||||||
|
removedSpaceNames.length > 0
|
||||||
|
? `Access to the following spaces were deleted: ${removedSpaceNames.join(', ')}`
|
||||||
|
: '';
|
||||||
|
|
||||||
|
const roleChanged =
|
||||||
|
oldRole !== newRole
|
||||||
|
? `Your user role has been changed from [${oldRole}] to [${newRole}]`
|
||||||
|
: '';
|
||||||
|
|
||||||
|
const nameChanged =
|
||||||
|
oldName !== newName
|
||||||
|
? `The name associated with your account has changed from [${oldName}] to [${newName}]`
|
||||||
|
: '';
|
||||||
|
|
||||||
|
return {
|
||||||
|
addedSpaceNamesChanged,
|
||||||
|
removedSpaceNamesChanged,
|
||||||
|
roleChanged,
|
||||||
|
nameChanged,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -151,7 +151,7 @@ export class UserAuthService {
|
|||||||
});
|
});
|
||||||
return res;
|
return res;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new BadRequestException('Invalid credentials');
|
throw new BadRequestException(error.message || 'Invalid credentials');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { InviteUserService } from '../services/invite-user.service';
|
|||||||
import {
|
import {
|
||||||
Body,
|
Body,
|
||||||
Controller,
|
Controller,
|
||||||
|
Delete,
|
||||||
Param,
|
Param,
|
||||||
Post,
|
Post,
|
||||||
Put,
|
Put,
|
||||||
@ -17,7 +18,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 +97,35 @@ 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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@UseGuards(JwtAuthGuard)
|
||||||
|
@Delete(':invitedUserUuid')
|
||||||
|
@ApiOperation({
|
||||||
|
summary: ControllerRoute.INVITE_USER.ACTIONS.DELETE_USER_INVITATION_SUMMARY,
|
||||||
|
description:
|
||||||
|
ControllerRoute.INVITE_USER.ACTIONS.DELETE_USER_INVITATION_DESCRIPTION,
|
||||||
|
})
|
||||||
|
async deleteUserInvitation(
|
||||||
|
@Param('invitedUserUuid') invitedUserUuid: string,
|
||||||
|
): Promise<BaseResponseDto> {
|
||||||
|
return await this.inviteUserService.deleteUserInvitation(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -25,6 +25,7 @@ import { UserDevicePermissionService } from 'src/user-device-permission/services
|
|||||||
import { DeviceUserPermissionRepository } from '@app/common/modules/device/repositories';
|
import { DeviceUserPermissionRepository } from '@app/common/modules/device/repositories';
|
||||||
import { PermissionTypeRepository } from '@app/common/modules/permission/repositories';
|
import { PermissionTypeRepository } from '@app/common/modules/permission/repositories';
|
||||||
import { ProjectUserService } from 'src/project/services/project-user.service';
|
import { ProjectUserService } from 'src/project/services/project-user.service';
|
||||||
|
import { RoleTypeRepository } from '@app/common/modules/role-type/repositories';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [ConfigModule, InviteUserRepositoryModule],
|
imports: [ConfigModule, InviteUserRepositoryModule],
|
||||||
@ -49,6 +50,7 @@ import { ProjectUserService } from 'src/project/services/project-user.service';
|
|||||||
DeviceUserPermissionRepository,
|
DeviceUserPermissionRepository,
|
||||||
PermissionTypeRepository,
|
PermissionTypeRepository,
|
||||||
ProjectUserService,
|
ProjectUserService,
|
||||||
|
RoleTypeRepository,
|
||||||
],
|
],
|
||||||
exports: [InviteUserService],
|
exports: [InviteUserService],
|
||||||
})
|
})
|
||||||
|
|||||||
@ -24,7 +24,11 @@ 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';
|
||||||
|
import { RoleTypeRepository } from '@app/common/modules/role-type/repositories';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class InviteUserService {
|
export class InviteUserService {
|
||||||
@ -36,6 +40,7 @@ export class InviteUserService {
|
|||||||
private readonly userSpaceService: UserSpaceService,
|
private readonly userSpaceService: UserSpaceService,
|
||||||
private readonly spaceUserService: SpaceUserService,
|
private readonly spaceUserService: SpaceUserService,
|
||||||
private readonly spaceRepository: SpaceRepository,
|
private readonly spaceRepository: SpaceRepository,
|
||||||
|
private readonly roleTypeRepository: RoleTypeRepository,
|
||||||
private readonly dataSource: DataSource,
|
private readonly dataSource: DataSource,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@ -111,7 +116,7 @@ export class InviteUserService {
|
|||||||
|
|
||||||
await Promise.all(spacePromises);
|
await Promise.all(spacePromises);
|
||||||
await this.emailService.sendEmailWithInvitationTemplate(email, {
|
await this.emailService.sendEmailWithInvitationTemplate(email, {
|
||||||
name: firstName + ' ' + lastName,
|
name: firstName,
|
||||||
invitationCode,
|
invitationCode,
|
||||||
role: roleType,
|
role: roleType,
|
||||||
spacesList: spaceNamesString,
|
spacesList: spaceNamesString,
|
||||||
@ -269,7 +274,7 @@ export class InviteUserService {
|
|||||||
dto: UpdateUserInvitationDto,
|
dto: UpdateUserInvitationDto,
|
||||||
invitedUserUuid: string,
|
invitedUserUuid: string,
|
||||||
): Promise<BaseResponseDto> {
|
): Promise<BaseResponseDto> {
|
||||||
const { projectUuid } = dto;
|
const { projectUuid, spaceUuids } = dto;
|
||||||
|
|
||||||
const queryRunner = this.dataSource.createQueryRunner();
|
const queryRunner = this.dataSource.createQueryRunner();
|
||||||
await queryRunner.startTransaction();
|
await queryRunner.startTransaction();
|
||||||
@ -278,6 +283,7 @@ export class InviteUserService {
|
|||||||
// Fetch the user's existing data in the project
|
// Fetch the user's existing data in the project
|
||||||
const userOldData = await this.inviteUserRepository.findOne({
|
const userOldData = await this.inviteUserRepository.findOne({
|
||||||
where: { uuid: invitedUserUuid, project: { uuid: projectUuid } },
|
where: { uuid: invitedUserUuid, project: { uuid: projectUuid } },
|
||||||
|
relations: ['project', 'spaces.space', 'roleType'],
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!userOldData) {
|
if (!userOldData) {
|
||||||
@ -285,19 +291,66 @@ 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 ||
|
await this.updateWhenUserIsInvite(queryRunner, dto, invitedUserUuid);
|
||||||
userOldData.status === UserStatusEnum.DISABLED
|
|
||||||
) {
|
|
||||||
await this.updateWhenUserIsInviteOrDisable(
|
|
||||||
queryRunner,
|
|
||||||
dto,
|
|
||||||
invitedUserUuid,
|
|
||||||
);
|
|
||||||
} else if (userOldData.status === UserStatusEnum.ACTIVE) {
|
} else if (userOldData.status === UserStatusEnum.ACTIVE) {
|
||||||
|
await this.updateWhenUserIsInvite(queryRunner, dto, invitedUserUuid);
|
||||||
await this.updateWhenUserIsActive(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,
|
||||||
|
emailMessage,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Proceed with other updates (e.g., roles, names, etc.)
|
||||||
await queryRunner.commitTransaction();
|
await queryRunner.commitTransaction();
|
||||||
|
|
||||||
return new SuccessResponseDto({
|
return new SuccessResponseDto({
|
||||||
@ -319,8 +372,15 @@ export class InviteUserService {
|
|||||||
await queryRunner.release();
|
await queryRunner.release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private async getRoleTypeByUuid(roleUuid: string) {
|
||||||
|
const role = await this.roleTypeRepository.findOne({
|
||||||
|
where: { uuid: roleUuid },
|
||||||
|
});
|
||||||
|
|
||||||
private async updateWhenUserIsInviteOrDisable(
|
return role.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async updateWhenUserIsInvite(
|
||||||
queryRunner: QueryRunner,
|
queryRunner: QueryRunner,
|
||||||
dto: UpdateUserInvitationDto,
|
dto: UpdateUserInvitationDto,
|
||||||
invitedUserUuid: string,
|
invitedUserUuid: string,
|
||||||
@ -475,4 +535,209 @@ 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 this.emailService.sendEmailWithTemplate(
|
||||||
|
userData.email,
|
||||||
|
userData.firstName,
|
||||||
|
disable,
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.disassociateUserFromSpaces(user, userData.project.uuid);
|
||||||
|
await this.inviteUserRepository.update(
|
||||||
|
{ uuid: invitedUserUuid },
|
||||||
|
{ isActive: false },
|
||||||
|
);
|
||||||
|
await this.userRepository.update(
|
||||||
|
{ uuid: user.uuid },
|
||||||
|
{ isActive: false },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await this.emailService.sendEmailWithTemplate(
|
||||||
|
userData.email,
|
||||||
|
userData.firstName,
|
||||||
|
false,
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import { BaseResponseDto } from '@app/common/dto/base.response.dto';
|
|||||||
import { SuccessResponseDto } from '@app/common/dto/success.response.dto';
|
import { SuccessResponseDto } from '@app/common/dto/success.response.dto';
|
||||||
import { InviteUserRepository } from '@app/common/modules/Invite-user/repositiories';
|
import { InviteUserRepository } from '@app/common/modules/Invite-user/repositiories';
|
||||||
import { ProjectService } from './project.service';
|
import { ProjectService } from './project.service';
|
||||||
import { UserStatusEnum } from '@app/common/constants/user-status.enum';
|
|
||||||
import { UserSpaceRepository } from '@app/common/modules/user/repositories';
|
import { UserSpaceRepository } from '@app/common/modules/user/repositories';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ -35,6 +34,7 @@ export class ProjectUserService {
|
|||||||
'phoneNumber',
|
'phoneNumber',
|
||||||
'jobTitle',
|
'jobTitle',
|
||||||
'invitedBy',
|
'invitedBy',
|
||||||
|
'isEnabled',
|
||||||
],
|
],
|
||||||
relations: ['roleType'],
|
relations: ['roleType'],
|
||||||
});
|
});
|
||||||
@ -73,7 +73,6 @@ export class ProjectUserService {
|
|||||||
invitedUserUuid: string,
|
invitedUserUuid: string,
|
||||||
): Promise<BaseResponseDto> {
|
): Promise<BaseResponseDto> {
|
||||||
try {
|
try {
|
||||||
let userSpaces;
|
|
||||||
const user = await this.inviteUserRepository.findOne({
|
const user = await this.inviteUserRepository.findOne({
|
||||||
where: {
|
where: {
|
||||||
project: { uuid: projectUuid },
|
project: { uuid: projectUuid },
|
||||||
@ -90,6 +89,7 @@ export class ProjectUserService {
|
|||||||
'phoneNumber',
|
'phoneNumber',
|
||||||
'jobTitle',
|
'jobTitle',
|
||||||
'invitedBy',
|
'invitedBy',
|
||||||
|
'isEnabled',
|
||||||
],
|
],
|
||||||
relations: ['roleType', 'spaces.space'],
|
relations: ['roleType', 'spaces.space'],
|
||||||
});
|
});
|
||||||
@ -100,15 +100,7 @@ export class ProjectUserService {
|
|||||||
HttpStatus.NOT_FOUND,
|
HttpStatus.NOT_FOUND,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (user.status === UserStatusEnum.ACTIVE) {
|
|
||||||
const spaces = await this.userSpaceRepository.find({
|
|
||||||
where: { user: { inviteUser: { uuid: invitedUserUuid } } },
|
|
||||||
relations: ['space'],
|
|
||||||
});
|
|
||||||
userSpaces = spaces.map((space) => space.space);
|
|
||||||
} else {
|
|
||||||
userSpaces = user.spaces.map((space) => space.space);
|
|
||||||
}
|
|
||||||
const createdAt = new Date(user.createdAt);
|
const createdAt = new Date(user.createdAt);
|
||||||
const createdDate = createdAt.toLocaleDateString();
|
const createdDate = createdAt.toLocaleDateString();
|
||||||
const createdTime = createdAt.toLocaleTimeString();
|
const createdTime = createdAt.toLocaleTimeString();
|
||||||
@ -119,7 +111,7 @@ export class ProjectUserService {
|
|||||||
roleType: user.roleType.type,
|
roleType: user.roleType.type,
|
||||||
createdDate,
|
createdDate,
|
||||||
createdTime,
|
createdTime,
|
||||||
spaces: userSpaces,
|
spaces: user.spaces.map((space) => space.space),
|
||||||
},
|
},
|
||||||
statusCode: HttpStatus.OK,
|
statusCode: HttpStatus.OK,
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user