Merge branch 'dev' of https://github.com/SyncrowIOT/backend into feat/edit-space-model

This commit is contained in:
hannathkadher
2024-12-30 10:08:43 +04:00
14 changed files with 135 additions and 5 deletions

View File

@ -8,6 +8,7 @@ import { UserRepository } from '@app/common/modules/user/repositories';
import { UserSessionRepository } from '@app/common/modules/session/repositories/session.repository';
import { UserOtpRepository } from '@app/common/modules/user/repositories';
import { RoleTypeRepository } from '@app/common/modules/role-type/repositories';
import { RoleService } from 'src/role/services';
@Module({
imports: [ConfigModule, UserRepositoryModule, CommonModule],
@ -18,6 +19,7 @@ import { RoleTypeRepository } from '@app/common/modules/role-type/repositories';
UserSessionRepository,
UserOtpRepository,
RoleTypeRepository,
RoleService,
],
exports: [UserAuthService],
})

View File

@ -18,6 +18,8 @@ import * as argon2 from 'argon2';
import { differenceInSeconds } from '@app/common/helper/differenceInSeconds';
import { LessThan, MoreThan } from 'typeorm';
import { ConfigService } from '@nestjs/config';
import { RoleService } from 'src/role/services';
import { RoleType } from '@app/common/constants/role.type.enum';
@Injectable()
export class UserAuthService {
@ -28,6 +30,7 @@ export class UserAuthService {
private readonly helperHashService: HelperHashService,
private readonly authService: AuthService,
private readonly emailService: EmailService,
private readonly roleService: RoleService,
private readonly configService: ConfigService,
) {}
@ -44,9 +47,13 @@ export class UserAuthService {
try {
const { regionUuid, ...rest } = userSignUpDto;
const spaceMemberRole = await this.roleService.findRoleByType(
RoleType.SPACE_MEMBER,
);
const user = await this.userRepository.save({
...rest,
password: hashedPassword,
roleType: { uuid: spaceMemberRole.uuid },
region: regionUuid
? {
uuid: regionUuid,

View File

@ -6,6 +6,8 @@ import { ControllerRoute } from '@app/common/constants/controller-route';
import { BaseResponseDto } from '@app/common/dto/base.response.dto';
import { PermissionsGuard } from 'src/guards/permissions.guard';
import { Permissions } from 'src/decorators/permissions.decorator';
import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard';
import { CheckEmailDto } from '../dtos/check-email.dto';
@ApiTags('Invite User Module')
@Controller({
@ -34,4 +36,19 @@ export class InviteUserController {
user.role.type,
);
}
@ApiBearerAuth()
@UseGuards(JwtAuthGuard)
@Post('check-email')
@ApiOperation({
summary: ControllerRoute.INVITE_USER.ACTIONS.CHECK_EMAIL_SUMMARY,
description: ControllerRoute.INVITE_USER.ACTIONS.CHECK_EMAIL_DESCRIPTION,
})
async checkEmailAndProject(
@Body() addUserInvitationDto: CheckEmailDto,
): Promise<BaseResponseDto> {
return await this.inviteUserService.checkEmailAndProject(
addUserInvitationDto,
);
}
}

View File

@ -61,6 +61,14 @@ export class AddUserInvitationDto {
@IsString()
@IsNotEmpty()
public roleUuid: string;
@ApiProperty({
description: 'The project uuid of the user',
example: 'd290f1ee-6c54-4b01-90e6-d701748f0851',
required: true,
})
@IsString()
@IsNotEmpty()
public projectUuid: string;
@ApiProperty({
description: 'The array of space UUIDs (at least one required)',
example: ['b5f3c9d2-58b7-4377-b3f7-60acb711d5d9'],

View File

@ -0,0 +1,16 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsEmail, IsNotEmpty } from 'class-validator';
export class CheckEmailDto {
@ApiProperty({
description: 'The email of the user',
example: 'OqM9A@example.com',
required: true,
})
@IsEmail()
@IsNotEmpty()
email: string;
constructor(dto: Partial<CheckEmailDto>) {
Object.assign(this, dto);
}
}

View File

@ -12,11 +12,14 @@ import {
InviteUserRepository,
InviteUserSpaceRepository,
} from '@app/common/modules/Invite-user/repositiories';
import { CheckEmailDto } from '../dtos/check-email.dto';
import { UserRepository } from '@app/common/modules/user/repositories';
@Injectable()
export class InviteUserService {
constructor(
private readonly inviteUserRepository: InviteUserRepository,
private readonly userRepository: UserRepository,
private readonly inviteUserSpaceRepository: InviteUserSpaceRepository,
private readonly dataSource: DataSource,
) {}
@ -33,6 +36,7 @@ export class InviteUserService {
phoneNumber,
roleUuid,
spaceUuids,
projectUuid,
} = dto;
const invitationCode = generateRandomString(6);
@ -67,6 +71,7 @@ export class InviteUserService {
status: UserStatusEnum.INVITED,
invitationCode,
invitedBy: roleType,
project: { uuid: projectUuid },
});
const invitedUser = await queryRunner.manager.save(inviteUser);
@ -105,4 +110,46 @@ export class InviteUserService {
await queryRunner.release();
}
}
async checkEmailAndProject(dto: CheckEmailDto): Promise<BaseResponseDto> {
const { email } = dto;
try {
const user = await this.userRepository.findOne({
where: { email },
relations: ['project'],
});
if (user?.project) {
throw new HttpException(
'This email already has a project',
HttpStatus.BAD_REQUEST,
);
}
const invitedUser = await this.inviteUserRepository.findOne({
where: { email },
relations: ['project'],
});
if (invitedUser?.project) {
throw new HttpException(
'This email already has a project',
HttpStatus.BAD_REQUEST,
);
}
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,
);
}
}
}

View File

@ -23,13 +23,15 @@ export class CreateOrphanSpaceHandler
async execute(command: CreateOrphanSpaceCommand): Promise<void> {
try {
const { project } = command;
const orphanCommunityName = `${ORPHAN_COMMUNITY_NAME}-${project.name}`;
let orphanCommunity = await this.communityRepository.findOne({
where: { name: ORPHAN_COMMUNITY_NAME, project },
where: { name: orphanCommunityName, project },
});
if (!orphanCommunity) {
orphanCommunity = this.communityRepository.create({
name: ORPHAN_COMMUNITY_NAME,
name: orphanCommunityName,
description: ORPHAN_COMMUNITY_DESCRIPTION,
project,
});

View File

@ -13,4 +13,12 @@ export class RoleService {
);
return roles;
}
async findRoleByType(roleType: RoleType) {
const role = await this.roleTypeRepository.findOne({
where: {
type: roleType,
},
});
return role;
}
}

View File

@ -1,4 +1,5 @@
import { TuyaService } from '@app/common/integrations/tuya/services/tuya.service';
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { GetDeviceDetailsInterface } from 'src/device/interfaces/get.device.interface';
import { GetSpaceParam } from '../dtos';
@ -25,6 +26,12 @@ export class SpaceDeviceService {
spaceUuid,
);
if (space.devices.length === 0) {
throw new HttpException(
'The space does not contain any devices.',
HttpStatus.BAD_REQUEST,
);
}
const safeFetch = async (device: any) => {
try {
const tuyaDetails = await this.getDeviceDetailsByDeviceIdTuya(
@ -42,7 +49,7 @@ export class SpaceDeviceService {
};
} catch (error) {
console.warn(
`Skipping device with deviceTuyaUuid: ${device.deviceTuyaUuid} due to error.`,
`Skipping device with deviceTuyaUuid: ${device.deviceTuyaUuid} due to error. ${error}`,
);
return null;
}