mirror of
https://github.com/SyncrowIOT/backend.git
synced 2025-07-15 18:27:05 +00:00
268 lines
8.4 KiB
TypeScript
268 lines
8.4 KiB
TypeScript
import {
|
|
BadRequestException,
|
|
HttpException,
|
|
HttpStatus,
|
|
Injectable,
|
|
} from '@nestjs/common';
|
|
import { UserSpaceRepository } from '@app/common/modules/user/repositories';
|
|
import { SuccessResponseDto } from '@app/common/dto/success.response.dto';
|
|
import { BaseResponseDto } from '@app/common/dto/base.response.dto';
|
|
import { AddUserSpaceDto, AddUserSpaceUsingCodeDto } from '../dtos';
|
|
import {
|
|
InviteSpaceRepository,
|
|
SpaceRepository,
|
|
} from '@app/common/modules/space/repositories';
|
|
import { CommonErrorCodes } from '@app/common/constants/error-codes.enum';
|
|
import { UserDevicePermissionService } from 'src/user-device-permission/services';
|
|
import { PermissionType } from '@app/common/constants/permission-type.enum';
|
|
import { InviteSpaceEntity } from '@app/common/modules/space/entities/invite-space.entity';
|
|
import { UserService } from './user.service';
|
|
import { RoleType } from '@app/common/constants/role.type.enum';
|
|
import {
|
|
InviteUserRepository,
|
|
InviteUserSpaceRepository,
|
|
} from '@app/common/modules/Invite-user/repositiories';
|
|
import { UserStatusEnum } from '@app/common/constants/user-status.enum';
|
|
|
|
@Injectable()
|
|
export class UserSpaceService {
|
|
constructor(
|
|
private readonly userSpaceRepository: UserSpaceRepository,
|
|
private readonly spaceRepository: SpaceRepository,
|
|
private readonly inviteSpaceRepository: InviteSpaceRepository,
|
|
private readonly userService: UserService,
|
|
private readonly inviteUserRepository: InviteUserRepository,
|
|
private readonly inviteUserSpaceRepository: InviteUserSpaceRepository,
|
|
private readonly userDevicePermissionService: UserDevicePermissionService,
|
|
) {}
|
|
|
|
async getSpacesForUser(userUuid: string): Promise<BaseResponseDto> {
|
|
let userSpaces = await this.userSpaceRepository.find({
|
|
where: { user: { uuid: userUuid } },
|
|
relations: ['space', 'space.community'],
|
|
});
|
|
|
|
if (!userSpaces || userSpaces.length === 0) {
|
|
userSpaces = [];
|
|
}
|
|
|
|
return new SuccessResponseDto({
|
|
data: userSpaces,
|
|
message: `Spaces for user ${userUuid} retrieved successfully`,
|
|
});
|
|
}
|
|
|
|
async verifyCodeAndAddUserSpace(
|
|
params: AddUserSpaceUsingCodeDto,
|
|
userUuid: string,
|
|
) {
|
|
const { inviteCode } = params;
|
|
try {
|
|
const inviteSpace = await this.findInviteSpaceByInviteCode(inviteCode);
|
|
const user = await this.userService.getUserDetailsByUserUuid(userUuid);
|
|
await this.checkSpaceMemberRole(user);
|
|
await this.addUserToSpace(userUuid, inviteSpace.space.uuid);
|
|
|
|
const deviceUUIDs = await this.getDeviceUUIDsForSpace(inviteSpace.uuid);
|
|
|
|
await this.addUserPermissionsToDevices(userUuid, deviceUUIDs);
|
|
await this.addUserAsActiveInvitation(
|
|
user,
|
|
inviteSpace.space.uuid,
|
|
inviteCode,
|
|
);
|
|
await this.clearSpaceInvitationCode(inviteSpace.uuid);
|
|
} catch (err) {
|
|
if (err instanceof HttpException) {
|
|
throw err;
|
|
} else {
|
|
throw new HttpException(
|
|
`An unexpected error occurred: ${err.message}`,
|
|
HttpStatus.INTERNAL_SERVER_ERROR,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
private async checkSpaceMemberRole(user: any) {
|
|
try {
|
|
if (user.role.type !== RoleType.SPACE_MEMBER) {
|
|
throw new BadRequestException(
|
|
'You have to be a space member to join this space',
|
|
);
|
|
}
|
|
} catch (err) {
|
|
throw new HttpException(
|
|
err.message || 'User not found',
|
|
err.status || HttpStatus.NOT_FOUND,
|
|
);
|
|
}
|
|
}
|
|
private async findInviteSpaceByInviteCode(
|
|
inviteCode: string,
|
|
): Promise<InviteSpaceEntity> {
|
|
try {
|
|
const inviteSpace = await this.inviteSpaceRepository.findOneOrFail({
|
|
where: {
|
|
invitationCode: inviteCode,
|
|
isActive: true,
|
|
},
|
|
relations: ['space'],
|
|
});
|
|
return inviteSpace;
|
|
} catch (error) {
|
|
throw new HttpException(
|
|
'Invalid invitation code',
|
|
HttpStatus.BAD_REQUEST,
|
|
);
|
|
}
|
|
}
|
|
private async clearSpaceInvitationCode(inviteSpaceUuid: string) {
|
|
await this.inviteSpaceRepository.update(
|
|
{ uuid: inviteSpaceUuid },
|
|
{ isActive: false },
|
|
);
|
|
}
|
|
async getProjectBySpaceUuid(spaceUuid: string) {
|
|
try {
|
|
const project = await this.spaceRepository.findOne({
|
|
where: {
|
|
uuid: spaceUuid,
|
|
},
|
|
relations: ['community.project'],
|
|
});
|
|
return project;
|
|
} catch (error) {
|
|
throw new HttpException('Space not found', HttpStatus.NOT_FOUND);
|
|
}
|
|
}
|
|
private async addUserAsActiveInvitation(
|
|
user: any,
|
|
spaceUuid: string,
|
|
inviteCode: string,
|
|
) {
|
|
try {
|
|
const space = await this.getProjectBySpaceUuid(spaceUuid);
|
|
const invitedUserData = await this.inviteUserRepository.findOne({
|
|
where: {
|
|
email: user.email,
|
|
project: { uuid: space.community.project.uuid },
|
|
},
|
|
});
|
|
if (!invitedUserData) {
|
|
const inviteUser = this.inviteUserRepository.create({
|
|
firstName: user.firstName,
|
|
lastName: user.lastName,
|
|
email: user.email,
|
|
jobTitle: null,
|
|
phoneNumber: null,
|
|
roleType: { uuid: user.role.uuid },
|
|
status: UserStatusEnum.ACTIVE,
|
|
invitationCode: inviteCode,
|
|
invitedBy: RoleType.SPACE_OWNER,
|
|
project: { uuid: space.community.project.uuid },
|
|
user: { uuid: user.uuid },
|
|
});
|
|
const invitedUser = await this.inviteUserRepository.save(inviteUser);
|
|
const inviteUserSpace = this.inviteUserSpaceRepository.create({
|
|
inviteUser: { uuid: invitedUser.uuid },
|
|
space: { uuid: spaceUuid },
|
|
});
|
|
|
|
await this.inviteUserSpaceRepository.save(inviteUserSpace);
|
|
} else {
|
|
const inviteUserSpace = this.inviteUserSpaceRepository.create({
|
|
inviteUser: { uuid: invitedUserData.uuid },
|
|
space: { uuid: spaceUuid },
|
|
});
|
|
|
|
await this.inviteUserSpaceRepository.save(inviteUserSpace);
|
|
}
|
|
} catch (err) {
|
|
throw new HttpException(
|
|
err.message || 'Failed to add user as an active invitation.',
|
|
HttpStatus.INTERNAL_SERVER_ERROR,
|
|
);
|
|
}
|
|
}
|
|
private async addUserToSpace(userUuid: string, spaceUuid: string) {
|
|
try {
|
|
const user = await this.addUserSpace({ userUuid, spaceUuid });
|
|
|
|
return user;
|
|
} catch (error) {
|
|
throw new HttpException(
|
|
`An error occurred while adding user to space ${spaceUuid} : ${error.message}}`,
|
|
HttpStatus.INTERNAL_SERVER_ERROR,
|
|
);
|
|
}
|
|
}
|
|
|
|
async addUserSpace(addUserSpaceDto: AddUserSpaceDto) {
|
|
try {
|
|
return await this.userSpaceRepository.save({
|
|
user: { uuid: addUserSpaceDto.userUuid },
|
|
space: { uuid: addUserSpaceDto.spaceUuid },
|
|
});
|
|
} catch (err) {
|
|
if (err.code === CommonErrorCodes.DUPLICATE_ENTITY) {
|
|
throw new HttpException(
|
|
'User already belongs to this unit',
|
|
HttpStatus.BAD_REQUEST,
|
|
);
|
|
}
|
|
throw new HttpException(
|
|
err.message || 'Internal Server Error',
|
|
HttpStatus.INTERNAL_SERVER_ERROR,
|
|
);
|
|
}
|
|
}
|
|
|
|
async getDeviceUUIDsForSpace(unitUuid: string): Promise<{ uuid: string }[]> {
|
|
const devices = await this.spaceRepository.find({
|
|
where: { uuid: unitUuid },
|
|
relations: ['devices', 'devices.productDevice'],
|
|
});
|
|
|
|
const allDevices = devices.flatMap((space) => space.devices);
|
|
|
|
return allDevices.map((device) => ({ uuid: device.uuid }));
|
|
}
|
|
|
|
async addUserPermissionsToDevices(
|
|
userUuid: string,
|
|
deviceUUIDs: { uuid: string }[],
|
|
): Promise<void> {
|
|
const permissionPromises = deviceUUIDs.map(async (device) => {
|
|
try {
|
|
await this.userDevicePermissionService.addUserPermission({
|
|
userUuid,
|
|
deviceUuid: device.uuid,
|
|
permissionType: PermissionType.CONTROLLABLE,
|
|
});
|
|
} catch (error) {
|
|
console.error(
|
|
`Failed to add permission for device ${device.uuid}: ${error.message}`,
|
|
);
|
|
}
|
|
});
|
|
|
|
await Promise.all(permissionPromises);
|
|
}
|
|
|
|
async deleteUserSpace(spaceUuid: string) {
|
|
try {
|
|
await this.userSpaceRepository
|
|
.createQueryBuilder('userSpace')
|
|
.leftJoin('userSpace.space', 'space')
|
|
.delete()
|
|
.where('space.uuid = :spaceUuid', { spaceUuid })
|
|
.execute();
|
|
} catch (error) {
|
|
throw new HttpException(
|
|
`Failed to delete user-space associations: ${error.message}`,
|
|
HttpStatus.INTERNAL_SERVER_ERROR,
|
|
);
|
|
}
|
|
}
|
|
}
|