diff --git a/src/app.module.ts b/src/app.module.ts index 568cff8..820602b 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -3,14 +3,10 @@ import { ConfigModule } from '@nestjs/config'; import config from './config'; import { AuthenticationModule } from './auth/auth.module'; import { UserModule } from './users/user.module'; -import { RoomModule } from './room/room.module'; import { GroupModule } from './group/group.module'; import { DeviceModule } from './device/device.module'; import { UserDevicePermissionModule } from './user-device-permission/user-device-permission.module'; import { CommunityModule } from './community/community.module'; -import { BuildingModule } from './building/building.module'; -import { FloorModule } from './floor/floor.module'; -import { UnitModule } from './unit/unit.module'; import { RoleModule } from './role/role.module'; import { SeederModule } from '@app/common/seed/seeder.module'; import { UserNotificationModule } from './user-notification/user-notification.module'; @@ -34,12 +30,9 @@ import { SpaceModule } from './space/space.module'; UserModule, RoleModule, CommunityModule, - BuildingModule, - FloorModule, - UnitModule, + SpaceModule, - RoomModule, - RoomModule, + GroupModule, DeviceModule, DeviceMessagesSubscriptionModule, diff --git a/src/automation/services/automation.service.ts b/src/automation/services/automation.service.ts index 3f5a4d5..95d9459 100644 --- a/src/automation/services/automation.service.ts +++ b/src/automation/services/automation.service.ts @@ -10,7 +10,6 @@ import { UpdateAutomationDto, UpdateAutomationStatusDto, } from '../dtos'; -import { GetUnitByUuidInterface } from 'src/unit/interface/unit.interface'; import { ConfigService } from '@nestjs/config'; import { TuyaContext } from '@tuya/tuya-connector-nodejs'; import { convertKeysToSnakeCase } from '@app/common/helper/snakeCaseConverter'; @@ -23,7 +22,6 @@ import { GetAutomationByUnitInterface, } from '../interface/automation.interface'; import { convertKeysToCamelCase } from '@app/common/helper/camelCaseConverter'; -import { SpaceType } from '@app/common/constants/space-type.enum'; import { ActionExecutorEnum, EntityTypeEnum, @@ -126,18 +124,14 @@ export class AutomationService { } } } - async getUnitByUuid(unitUuid: string): Promise { + async getUnitByUuid(unitUuid: string) { try { const unit = await this.spaceRepository.findOne({ where: { uuid: unitUuid, - spaceType: { - type: SpaceType.UNIT, - }, }, - relations: ['spaceType'], }); - if (!unit || !unit.spaceType || unit.spaceType.type !== SpaceType.UNIT) { + if (!unit) { throw new BadRequestException('Invalid unit UUID'); } return { @@ -145,7 +139,6 @@ export class AutomationService { createdAt: unit.createdAt, updatedAt: unit.updatedAt, name: unit.spaceName, - type: unit.spaceType.type, spaceTuyaUuid: unit.spaceTuyaUuid, }; } catch (err) { diff --git a/src/building/building.module.ts b/src/building/building.module.ts deleted file mode 100644 index a78c049..0000000 --- a/src/building/building.module.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Module } from '@nestjs/common'; -import { BuildingService } from './services/building.service'; -import { BuildingController } from './controllers/building.controller'; -import { ConfigModule } from '@nestjs/config'; -import { SpaceRepositoryModule } from '@app/common/modules/space/space.repository.module'; -import { SpaceRepository } from '@app/common/modules/space/repositories'; -import { SpaceTypeRepository } from '@app/common/modules/space/repositories'; -import { UserSpaceRepository } from '@app/common/modules/user/repositories'; -import { UserRepository } from '@app/common/modules/user/repositories'; -import { UserRepositoryModule } from '@app/common/modules/user/user.repository.module'; - -@Module({ - imports: [ConfigModule, SpaceRepositoryModule, UserRepositoryModule], - controllers: [BuildingController], - providers: [ - BuildingService, - SpaceRepository, - SpaceTypeRepository, - UserSpaceRepository, - UserRepository, - ], - exports: [BuildingService], -}) -export class BuildingModule {} diff --git a/src/building/controllers/building.controller.ts b/src/building/controllers/building.controller.ts deleted file mode 100644 index 8c7d1b1..0000000 --- a/src/building/controllers/building.controller.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { BuildingService } from '../services/building.service'; -import { - Body, - Controller, - Get, - HttpStatus, - Param, - Post, - Put, - Query, - UseGuards, -} from '@nestjs/common'; -import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; -import { AddBuildingDto, AddUserBuildingDto } from '../dtos/add.building.dto'; -import { GetBuildingChildDto } from '../dtos/get.building.dto'; -import { UpdateBuildingNameDto } from '../dtos/update.building.dto'; -import { CheckCommunityTypeGuard } from 'src/guards/community.type.guard'; -import { CheckUserBuildingGuard } from 'src/guards/user.building.guard'; -import { AdminRoleGuard } from 'src/guards/admin.role.guard'; -import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; -import { BuildingPermissionGuard } from 'src/guards/building.permission.guard'; -import { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; -import { SpaceType } from '@app/common/constants/space-type.enum'; - -@ApiTags('Building Module') -@Controller({ - version: EnableDisableStatusEnum.ENABLED, - path: SpaceType.BUILDING, -}) -export class BuildingController { - constructor(private readonly buildingService: BuildingService) {} - - @ApiBearerAuth() - @UseGuards(JwtAuthGuard, CheckCommunityTypeGuard) - @Post() - async addBuilding(@Body() addBuildingDto: AddBuildingDto) { - const building = await this.buildingService.addBuilding(addBuildingDto); - return { - statusCode: HttpStatus.CREATED, - success: true, - message: 'Building added successfully', - data: building, - }; - } - - @ApiBearerAuth() - @UseGuards(JwtAuthGuard, BuildingPermissionGuard) - @Get(':buildingUuid') - async getBuildingByUuid(@Param('buildingUuid') buildingUuid: string) { - const building = await this.buildingService.getBuildingByUuid(buildingUuid); - return building; - } - - @ApiBearerAuth() - @UseGuards(JwtAuthGuard, BuildingPermissionGuard) - @Get('child/:buildingUuid') - async getBuildingChildByUuid( - @Param('buildingUuid') buildingUuid: string, - @Query() query: GetBuildingChildDto, - ) { - const building = await this.buildingService.getBuildingChildByUuid( - buildingUuid, - query, - ); - return building; - } - @ApiBearerAuth() - @UseGuards(JwtAuthGuard, BuildingPermissionGuard) - @Get('parent/:buildingUuid') - async getBuildingParentByUuid(@Param('buildingUuid') buildingUuid: string) { - const building = - await this.buildingService.getBuildingParentByUuid(buildingUuid); - return building; - } - @ApiBearerAuth() - @UseGuards(AdminRoleGuard, CheckUserBuildingGuard) - @Post('user') - async addUserBuilding(@Body() addUserBuildingDto: AddUserBuildingDto) { - await this.buildingService.addUserBuilding(addUserBuildingDto); - return { - statusCode: HttpStatus.CREATED, - success: true, - message: 'user building added successfully', - }; - } - @ApiBearerAuth() - @UseGuards(JwtAuthGuard) - @Get('user/:userUuid') - async getBuildingsByUserId(@Param('userUuid') userUuid: string) { - return await this.buildingService.getBuildingsByUserId(userUuid); - } - - @ApiBearerAuth() - @UseGuards(JwtAuthGuard, BuildingPermissionGuard) - @Put(':buildingUuid') - async renameBuildingByUuid( - @Param('buildingUuid') buildingUuid: string, - @Body() updateBuildingDto: UpdateBuildingNameDto, - ) { - const building = await this.buildingService.renameBuildingByUuid( - buildingUuid, - updateBuildingDto, - ); - return building; - } -} diff --git a/src/building/controllers/index.ts b/src/building/controllers/index.ts deleted file mode 100644 index b5ec3c2..0000000 --- a/src/building/controllers/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './building.controller'; diff --git a/src/building/dtos/add.building.dto.ts b/src/building/dtos/add.building.dto.ts deleted file mode 100644 index 5d79231..0000000 --- a/src/building/dtos/add.building.dto.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsNotEmpty, IsString } from 'class-validator'; - -export class AddBuildingDto { - @ApiProperty({ - description: 'buildingName', - required: true, - }) - @IsString() - @IsNotEmpty() - public buildingName: string; - - @ApiProperty({ - description: 'communityUuid', - required: true, - }) - @IsString() - @IsNotEmpty() - public communityUuid: string; - constructor(dto: Partial) { - Object.assign(this, dto); - } -} -export class AddUserBuildingDto { - @ApiProperty({ - description: 'buildingUuid', - required: true, - }) - @IsString() - @IsNotEmpty() - public buildingUuid: string; - @ApiProperty({ - description: 'userUuid', - required: true, - }) - @IsString() - @IsNotEmpty() - public userUuid: string; - constructor(dto: Partial) { - Object.assign(this, dto); - } -} diff --git a/src/building/dtos/get.building.dto.ts b/src/building/dtos/get.building.dto.ts deleted file mode 100644 index fae0c6b..0000000 --- a/src/building/dtos/get.building.dto.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { BooleanValues } from '@app/common/constants/boolean-values.enum'; -import { ApiProperty } from '@nestjs/swagger'; -import { Transform } from 'class-transformer'; -import { - IsBoolean, - IsInt, - IsNotEmpty, - IsOptional, - IsString, - Min, -} from 'class-validator'; - -export class GetBuildingDto { - @ApiProperty({ - description: 'buildingUuid', - required: true, - }) - @IsString() - @IsNotEmpty() - public buildingUuid: string; -} - -export class GetBuildingChildDto { - @ApiProperty({ example: 1, description: 'Page number', required: true }) - @IsInt({ message: 'Page must be a number' }) - @Min(1, { message: 'Page must not be less than 1' }) - @IsNotEmpty() - public page: number; - - @ApiProperty({ - example: 10, - description: 'Number of items per page', - required: true, - }) - @IsInt({ message: 'Page size must be a number' }) - @Min(1, { message: 'Page size must not be less than 1' }) - @IsNotEmpty() - public pageSize: number; - - @ApiProperty({ - example: true, - description: 'Flag to determine whether to fetch full hierarchy', - required: false, - default: false, - }) - @IsOptional() - @IsBoolean() - @Transform((value) => { - return value.obj.includeSubSpaces === BooleanValues.TRUE; - }) - public includeSubSpaces: boolean = false; -} diff --git a/src/building/dtos/index.ts b/src/building/dtos/index.ts deleted file mode 100644 index 93e7c6f..0000000 --- a/src/building/dtos/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './add.building.dto'; diff --git a/src/building/dtos/update.building.dto.ts b/src/building/dtos/update.building.dto.ts deleted file mode 100644 index 0f07cbe..0000000 --- a/src/building/dtos/update.building.dto.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsNotEmpty, IsString } from 'class-validator'; - -export class UpdateBuildingNameDto { - @ApiProperty({ - description: 'buildingName', - required: true, - }) - @IsString() - @IsNotEmpty() - public buildingName: string; - - constructor(dto: Partial) { - Object.assign(this, dto); - } -} diff --git a/src/building/interface/building.interface.ts b/src/building/interface/building.interface.ts deleted file mode 100644 index 1127456..0000000 --- a/src/building/interface/building.interface.ts +++ /dev/null @@ -1,31 +0,0 @@ -export interface GetBuildingByUuidInterface { - uuid: string; - createdAt: Date; - updatedAt: Date; - name: string; - type: string; -} - -export interface BuildingChildInterface { - uuid: string; - name: string; - type: string; - totalCount?: number; - children?: BuildingChildInterface[]; -} -export interface BuildingParentInterface { - uuid: string; - name: string; - type: string; - parent?: BuildingParentInterface; -} -export interface RenameBuildingByUuidInterface { - uuid: string; - name: string; - type: string; -} -export interface GetBuildingByUserUuidInterface { - uuid: string; - name: string; - type: string; -} diff --git a/src/building/services/building.service.ts b/src/building/services/building.service.ts deleted file mode 100644 index b3de5d4..0000000 --- a/src/building/services/building.service.ts +++ /dev/null @@ -1,317 +0,0 @@ -import { GetBuildingChildDto } from '../dtos/get.building.dto'; -import { SpaceTypeRepository } from '../../../libs/common/src/modules/space/repositories/space.repository'; -import { - Injectable, - HttpException, - HttpStatus, - BadRequestException, -} from '@nestjs/common'; -import { SpaceRepository } from '@app/common/modules/space/repositories'; -import { AddBuildingDto, AddUserBuildingDto } from '../dtos'; -import { - BuildingChildInterface, - BuildingParentInterface, - GetBuildingByUserUuidInterface, - GetBuildingByUuidInterface, - RenameBuildingByUuidInterface, -} from '../interface/building.interface'; -import { SpaceEntity } from '@app/common/modules/space/entities'; -import { UpdateBuildingNameDto } from '../dtos/update.building.dto'; -import { UserSpaceRepository } from '@app/common/modules/user/repositories'; -import { SpaceType } from '@app/common/constants/space-type.enum'; -import { CommonErrorCodes } from '@app/common/constants/error-codes.enum'; - -@Injectable() -export class BuildingService { - constructor( - private readonly spaceRepository: SpaceRepository, - private readonly spaceTypeRepository: SpaceTypeRepository, - private readonly userSpaceRepository: UserSpaceRepository, - ) {} - - async addBuilding(addBuildingDto: AddBuildingDto) { - try { - const spaceType = await this.spaceTypeRepository.findOne({ - where: { - type: SpaceType.BUILDING, - }, - }); - - if (!spaceType) { - throw new BadRequestException('Invalid building UUID'); - } - const building = await this.spaceRepository.save({ - spaceName: addBuildingDto.buildingName, - parent: { uuid: addBuildingDto.communityUuid }, - spaceType: { uuid: spaceType.uuid }, - }); - return building; - } catch (err) { - if (err instanceof BadRequestException) { - throw err; // Re-throw BadRequestException - } else { - throw new HttpException('Building not found', HttpStatus.NOT_FOUND); - } - } - } - - async getBuildingByUuid( - buildingUuid: string, - ): Promise { - try { - const building = await this.spaceRepository.findOne({ - where: { - uuid: buildingUuid, - spaceType: { - type: SpaceType.BUILDING, - }, - }, - relations: ['spaceType'], - }); - if ( - !building || - !building.spaceType || - building.spaceType.type !== SpaceType.BUILDING - ) { - throw new BadRequestException('Invalid building UUID'); - } - return { - uuid: building.uuid, - createdAt: building.createdAt, - updatedAt: building.updatedAt, - name: building.spaceName, - type: building.spaceType.type, - }; - } catch (err) { - if (err instanceof BadRequestException) { - throw err; // Re-throw BadRequestException - } else { - throw new HttpException('Building not found', HttpStatus.NOT_FOUND); - } - } - } - async getBuildingChildByUuid( - buildingUuid: string, - getBuildingChildDto: GetBuildingChildDto, - ): Promise { - try { - const { includeSubSpaces, page, pageSize } = getBuildingChildDto; - - const space = await this.spaceRepository.findOneOrFail({ - where: { uuid: buildingUuid }, - relations: ['children', 'spaceType'], - }); - if ( - !space || - !space.spaceType || - space.spaceType.type !== SpaceType.BUILDING - ) { - throw new BadRequestException('Invalid building UUID'); - } - - const totalCount = await this.spaceRepository.count({ - where: { parent: { uuid: space.uuid } }, - }); - - const children = await this.buildHierarchy( - space, - includeSubSpaces, - page, - pageSize, - ); - - return { - uuid: space.uuid, - name: space.spaceName, - type: space.spaceType.type, - totalCount, - children, - }; - } catch (err) { - if (err instanceof BadRequestException) { - throw err; // Re-throw BadRequestException - } else { - throw new HttpException('Building not found', HttpStatus.NOT_FOUND); - } - } - } - - private async buildHierarchy( - space: SpaceEntity, - includeSubSpaces: boolean, - page: number, - pageSize: number, - ): Promise { - const children = await this.spaceRepository.find({ - where: { parent: { uuid: space.uuid } }, - relations: ['spaceType'], - skip: (page - 1) * pageSize, - take: pageSize, - }); - - if (!children || children.length === 0 || !includeSubSpaces) { - return children - .filter( - (child) => - child.spaceType.type !== SpaceType.BUILDING && - child.spaceType.type !== SpaceType.COMMUNITY, - ) // Filter remaining building and community types - .map((child) => ({ - uuid: child.uuid, - name: child.spaceName, - type: child.spaceType.type, - })); - } - - const childHierarchies = await Promise.all( - children - .filter( - (child) => - child.spaceType.type !== SpaceType.BUILDING && - child.spaceType.type !== SpaceType.COMMUNITY, - ) // Filter remaining building and community types - .map(async (child) => ({ - uuid: child.uuid, - name: child.spaceName, - type: child.spaceType.type, - children: await this.buildHierarchy(child, true, 1, pageSize), - })), - ); - - return childHierarchies; - } - - async getBuildingParentByUuid( - buildingUuid: string, - ): Promise { - try { - const building = await this.spaceRepository.findOne({ - where: { - uuid: buildingUuid, - spaceType: { - type: SpaceType.BUILDING, - }, - }, - relations: ['spaceType', 'parent', 'parent.spaceType'], - }); - if ( - !building || - !building.spaceType || - building.spaceType.type !== SpaceType.BUILDING - ) { - throw new BadRequestException('Invalid building UUID'); - } - return { - uuid: building.uuid, - name: building.spaceName, - type: building.spaceType.type, - parent: { - uuid: building.parent.uuid, - name: building.parent.spaceName, - type: building.parent.spaceType.type, - }, - }; - } catch (err) { - if (err instanceof BadRequestException) { - throw err; // Re-throw BadRequestException - } else { - throw new HttpException('Building not found', HttpStatus.NOT_FOUND); - } - } - } - - async getBuildingsByUserId( - userUuid: string, - ): Promise { - try { - const buildings = await this.userSpaceRepository.find({ - relations: ['space', 'space.spaceType'], - where: { - user: { uuid: userUuid }, - space: { spaceType: { type: SpaceType.BUILDING } }, - }, - }); - - if (buildings.length === 0) { - throw new HttpException( - 'this user has no buildings', - HttpStatus.NOT_FOUND, - ); - } - const spaces = buildings.map((building) => ({ - uuid: building.space.uuid, - name: building.space.spaceName, - type: building.space.spaceType.type, - })); - - return spaces; - } catch (err) { - if (err instanceof HttpException) { - throw err; - } else { - throw new HttpException('user not found', HttpStatus.NOT_FOUND); - } - } - } - async addUserBuilding(addUserBuildingDto: AddUserBuildingDto) { - try { - await this.userSpaceRepository.save({ - user: { uuid: addUserBuildingDto.userUuid }, - space: { uuid: addUserBuildingDto.buildingUuid }, - }); - } catch (err) { - if (err.code === CommonErrorCodes.DUPLICATE_ENTITY) { - throw new HttpException( - 'User already belongs to this building', - HttpStatus.BAD_REQUEST, - ); - } - throw new HttpException( - err.message || 'Internal Server Error', - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } - async renameBuildingByUuid( - buildingUuid: string, - updateBuildingNameDto: UpdateBuildingNameDto, - ): Promise { - try { - const building = await this.spaceRepository.findOneOrFail({ - where: { uuid: buildingUuid }, - relations: ['spaceType'], - }); - - if ( - !building || - !building.spaceType || - building.spaceType.type !== SpaceType.BUILDING - ) { - throw new BadRequestException('Invalid building UUID'); - } - - await this.spaceRepository.update( - { uuid: buildingUuid }, - { spaceName: updateBuildingNameDto.buildingName }, - ); - - // Fetch the updated building - const updatedBuilding = await this.spaceRepository.findOneOrFail({ - where: { uuid: buildingUuid }, - relations: ['spaceType'], - }); - - return { - uuid: updatedBuilding.uuid, - name: updatedBuilding.spaceName, - type: updatedBuilding.spaceType.type, - }; - } catch (err) { - if (err instanceof BadRequestException) { - throw err; // Re-throw BadRequestException - } else { - throw new HttpException('Building not found', HttpStatus.NOT_FOUND); - } - } - } -} diff --git a/src/building/services/index.ts b/src/building/services/index.ts deleted file mode 100644 index 7b260d2..0000000 --- a/src/building/services/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './building.service'; diff --git a/src/device/services/device.service.ts b/src/device/services/device.service.ts index 25e34c6..9963383 100644 --- a/src/device/services/device.service.ts +++ b/src/device/services/device.service.ts @@ -38,6 +38,7 @@ import { DeviceStatusFirebaseService } from '@app/common/firebase/devices-status import { DeviceStatuses } from '@app/common/constants/device-status.enum'; import { CommonErrorCodes } from '@app/common/constants/error-codes.enum'; import { BatteryStatus } from '@app/common/constants/battery-status.enum'; +import { SpaceEntity } from '@app/common/modules/space/entities'; @Injectable() export class DeviceService { @@ -836,6 +837,7 @@ export class DeviceService { 'permission.permissionType', ], }); + const devicesData = await Promise.allSettled( devices.map(async (device) => { let battery = null; @@ -880,22 +882,26 @@ export class DeviceService { battery = batteryStatus.value; } } - const spaceDevice = device?.spaceDevice; - return { - space: { - uuid: spaceDevice?.uuid, - name: spaceDevice?.spaceName, - }, + const spaceHierarchy = await this.getFullSpaceHierarchy( + device?.spaceDevice, + ); + const orderedHierarchy = spaceHierarchy.reverse(); + + return { + spaces: orderedHierarchy.map((space) => ({ + uuid: space.uuid, + spaceName: space.spaceName, + })), productUuid: device.productDevice.uuid, productType: device.productDevice.prodType, - permissionType: device.permission[0].permissionType.type, - ...(await this.getDeviceDetailsByDeviceIdTuya( + // permissionType: device.permission[0].permissionType.type, + /* ...(await this.getDeviceDetailsByDeviceIdTuya( device.deviceTuyaUuid, - )), + )),*/ uuid: device.uuid, ...(battery && { battery }), - } as GetDeviceDetailsInterface; + }; }), ); @@ -969,4 +975,83 @@ export class DeviceService { ); } } + + async getFullSpaceHierarchy( + space: SpaceEntity, + ): Promise<{ uuid: string; spaceName: string }[]> { + try { + console.log('Fetching hierarchy for space:', space.uuid); + + // Fetch only the relevant spaces, starting with the target space + const targetSpace = await this.spaceRepository.findOne({ + where: { uuid: space.uuid }, + relations: ['parent', 'children'], + }); + + // Fetch only the ancestors of the target space + const ancestors = await this.fetchAncestors(targetSpace); + + // Optionally, fetch descendants if required + const descendants = await this.fetchDescendants(targetSpace); + + const fullHierarchy = [...ancestors, targetSpace, ...descendants].map( + (space) => ({ + uuid: space.uuid, + spaceName: space.spaceName, + }), + ); + + return fullHierarchy; + } catch (error) { + console.error('Error fetching space hierarchy:', error.message); + throw new HttpException( + 'Error fetching space hierarchy', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + private async fetchAncestors(space: SpaceEntity): Promise { + const ancestors: SpaceEntity[] = []; + + let currentSpace = space; + while (currentSpace && currentSpace.parent) { + // Fetch the parent space + const parent = await this.spaceRepository.findOne({ + where: { uuid: currentSpace.parent.uuid }, + relations: ['parent'], // To continue fetching upwards + }); + + if (parent) { + ancestors.push(parent); + currentSpace = parent; + } else { + currentSpace = null; + } + } + + // Return the ancestors in reverse order to have the root at the start + return ancestors.reverse(); + } + + private async fetchDescendants(space: SpaceEntity): Promise { + const descendants: SpaceEntity[] = []; + + // Fetch the immediate children of the current space + const children = await this.spaceRepository.find({ + where: { parent: { uuid: space.uuid } }, + relations: ['children'], // To continue fetching downwards + }); + + for (const child of children) { + // Add the child to the descendants list + descendants.push(child); + + // Recursively fetch the child's descendants + const childDescendants = await this.fetchDescendants(child); + descendants.push(...childDescendants); + } + + return descendants; + } } diff --git a/src/floor/controllers/floor.controller.ts b/src/floor/controllers/floor.controller.ts deleted file mode 100644 index 9142db6..0000000 --- a/src/floor/controllers/floor.controller.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { FloorService } from '../services/floor.service'; -import { - Body, - Controller, - Get, - HttpStatus, - Param, - Post, - Put, - Query, - UseGuards, -} from '@nestjs/common'; -import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; -import { AddFloorDto, AddUserFloorDto } from '../dtos/add.floor.dto'; -import { GetFloorChildDto } from '../dtos/get.floor.dto'; -import { UpdateFloorNameDto } from '../dtos/update.floor.dto'; -import { CheckBuildingTypeGuard } from 'src/guards/building.type.guard'; -import { CheckUserFloorGuard } from 'src/guards/user.floor.guard'; -import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; -import { AdminRoleGuard } from 'src/guards/admin.role.guard'; -import { FloorPermissionGuard } from 'src/guards/floor.permission.guard'; -import { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; -import { SpaceType } from '@app/common/constants/space-type.enum'; - -@ApiTags('Floor Module') -@Controller({ - version: EnableDisableStatusEnum.ENABLED, - path: SpaceType.FLOOR, -}) -export class FloorController { - constructor(private readonly floorService: FloorService) {} - - @ApiBearerAuth() - @UseGuards(JwtAuthGuard, CheckBuildingTypeGuard) - @Post() - async addFloor(@Body() addFloorDto: AddFloorDto) { - const floor = await this.floorService.addFloor(addFloorDto); - return { - statusCode: HttpStatus.CREATED, - success: true, - message: 'Floor added successfully', - data: floor, - }; - } - - @ApiBearerAuth() - @UseGuards(JwtAuthGuard, FloorPermissionGuard) - @Get(':floorUuid') - async getFloorByUuid(@Param('floorUuid') floorUuid: string) { - const floor = await this.floorService.getFloorByUuid(floorUuid); - return floor; - } - - @ApiBearerAuth() - @UseGuards(JwtAuthGuard, FloorPermissionGuard) - @Get('child/:floorUuid') - async getFloorChildByUuid( - @Param('floorUuid') floorUuid: string, - @Query() query: GetFloorChildDto, - ) { - const floor = await this.floorService.getFloorChildByUuid(floorUuid, query); - return floor; - } - @ApiBearerAuth() - @UseGuards(JwtAuthGuard, FloorPermissionGuard) - @Get('parent/:floorUuid') - async getFloorParentByUuid(@Param('floorUuid') floorUuid: string) { - const floor = await this.floorService.getFloorParentByUuid(floorUuid); - return floor; - } - - @ApiBearerAuth() - @UseGuards(AdminRoleGuard, CheckUserFloorGuard) - @Post('user') - async addUserFloor(@Body() addUserFloorDto: AddUserFloorDto) { - await this.floorService.addUserFloor(addUserFloorDto); - return { - statusCode: HttpStatus.CREATED, - success: true, - message: 'user floor added successfully', - }; - } - - @ApiBearerAuth() - @UseGuards(JwtAuthGuard) - @Get('user/:userUuid') - async getFloorsByUserId(@Param('userUuid') userUuid: string) { - return await this.floorService.getFloorsByUserId(userUuid); - } - - @ApiBearerAuth() - @UseGuards(JwtAuthGuard, FloorPermissionGuard) - @Put(':floorUuid') - async renameFloorByUuid( - @Param('floorUuid') floorUuid: string, - @Body() updateFloorNameDto: UpdateFloorNameDto, - ) { - const floor = await this.floorService.renameFloorByUuid( - floorUuid, - updateFloorNameDto, - ); - return floor; - } -} diff --git a/src/floor/controllers/index.ts b/src/floor/controllers/index.ts deleted file mode 100644 index 99eb600..0000000 --- a/src/floor/controllers/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './floor.controller'; diff --git a/src/floor/dtos/add.floor.dto.ts b/src/floor/dtos/add.floor.dto.ts deleted file mode 100644 index 3d1655a..0000000 --- a/src/floor/dtos/add.floor.dto.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsNotEmpty, IsString } from 'class-validator'; - -export class AddFloorDto { - @ApiProperty({ - description: 'floorName', - required: true, - }) - @IsString() - @IsNotEmpty() - public floorName: string; - - @ApiProperty({ - description: 'buildingUuid', - required: true, - }) - @IsString() - @IsNotEmpty() - public buildingUuid: string; - constructor(dto: Partial) { - Object.assign(this, dto); - } -} -export class AddUserFloorDto { - @ApiProperty({ - description: 'floorUuid', - required: true, - }) - @IsString() - @IsNotEmpty() - public floorUuid: string; - @ApiProperty({ - description: 'userUuid', - required: true, - }) - @IsString() - @IsNotEmpty() - public userUuid: string; - constructor(dto: Partial) { - Object.assign(this, dto); - } -} diff --git a/src/floor/dtos/get.floor.dto.ts b/src/floor/dtos/get.floor.dto.ts deleted file mode 100644 index 957f81b..0000000 --- a/src/floor/dtos/get.floor.dto.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { BooleanValues } from '@app/common/constants/boolean-values.enum'; -import { ApiProperty } from '@nestjs/swagger'; -import { Transform } from 'class-transformer'; -import { - IsBoolean, - IsInt, - IsNotEmpty, - IsOptional, - IsString, - Min, -} from 'class-validator'; - -export class GetFloorDto { - @ApiProperty({ - description: 'floorUuid', - required: true, - }) - @IsString() - @IsNotEmpty() - public floorUuid: string; -} - -export class GetFloorChildDto { - @ApiProperty({ example: 1, description: 'Page number', required: true }) - @IsInt({ message: 'Page must be a number' }) - @Min(1, { message: 'Page must not be less than 1' }) - @IsNotEmpty() - public page: number; - - @ApiProperty({ - example: 10, - description: 'Number of items per page', - required: true, - }) - @IsInt({ message: 'Page size must be a number' }) - @Min(1, { message: 'Page size must not be less than 1' }) - @IsNotEmpty() - public pageSize: number; - - @ApiProperty({ - example: true, - description: 'Flag to determine whether to fetch full hierarchy', - required: false, - default: false, - }) - @IsOptional() - @IsBoolean() - @Transform((value) => { - return value.obj.includeSubSpaces === BooleanValues.TRUE; - }) - public includeSubSpaces: boolean = false; -} diff --git a/src/floor/dtos/index.ts b/src/floor/dtos/index.ts deleted file mode 100644 index 9c08a9f..0000000 --- a/src/floor/dtos/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './add.floor.dto'; diff --git a/src/floor/dtos/update.floor.dto.ts b/src/floor/dtos/update.floor.dto.ts deleted file mode 100644 index 11c97b0..0000000 --- a/src/floor/dtos/update.floor.dto.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsNotEmpty, IsString } from 'class-validator'; - -export class UpdateFloorNameDto { - @ApiProperty({ - description: 'floorName', - required: true, - }) - @IsString() - @IsNotEmpty() - public floorName: string; - - constructor(dto: Partial) { - Object.assign(this, dto); - } -} diff --git a/src/floor/floor.module.ts b/src/floor/floor.module.ts deleted file mode 100644 index 9fdc1c7..0000000 --- a/src/floor/floor.module.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Module } from '@nestjs/common'; -import { FloorService } from './services/floor.service'; -import { FloorController } from './controllers/floor.controller'; -import { ConfigModule } from '@nestjs/config'; -import { SpaceRepositoryModule } from '@app/common/modules/space/space.repository.module'; -import { SpaceRepository } from '@app/common/modules/space/repositories'; -import { SpaceTypeRepository } from '@app/common/modules/space/repositories'; -import { UserSpaceRepository } from '@app/common/modules/user/repositories'; -import { UserRepositoryModule } from '@app/common/modules/user/user.repository.module'; -import { UserRepository } from '@app/common/modules/user/repositories'; - -@Module({ - imports: [ConfigModule, SpaceRepositoryModule, UserRepositoryModule], - controllers: [FloorController], - providers: [ - FloorService, - SpaceRepository, - SpaceTypeRepository, - UserSpaceRepository, - UserRepository, - ], - exports: [FloorService], -}) -export class FloorModule {} diff --git a/src/floor/interface/floor.interface.ts b/src/floor/interface/floor.interface.ts deleted file mode 100644 index 37f35c4..0000000 --- a/src/floor/interface/floor.interface.ts +++ /dev/null @@ -1,32 +0,0 @@ -export interface GetFloorByUuidInterface { - uuid: string; - createdAt: Date; - updatedAt: Date; - name: string; - type: string; -} - -export interface FloorChildInterface { - uuid: string; - name: string; - type: string; - totalCount?: number; - children?: FloorChildInterface[]; -} -export interface FloorParentInterface { - uuid: string; - name: string; - type: string; - parent?: FloorParentInterface; -} -export interface RenameFloorByUuidInterface { - uuid: string; - name: string; - type: string; -} - -export interface GetFloorByUserUuidInterface { - uuid: string; - name: string; - type: string; -} diff --git a/src/floor/services/floor.service.ts b/src/floor/services/floor.service.ts deleted file mode 100644 index 1f7604f..0000000 --- a/src/floor/services/floor.service.ts +++ /dev/null @@ -1,310 +0,0 @@ -import { GetFloorChildDto } from '../dtos/get.floor.dto'; -import { SpaceTypeRepository } from '../../../libs/common/src/modules/space/repositories/space.repository'; -import { - Injectable, - HttpException, - HttpStatus, - BadRequestException, -} from '@nestjs/common'; -import { SpaceRepository } from '@app/common/modules/space/repositories'; -import { AddFloorDto, AddUserFloorDto } from '../dtos'; -import { - FloorChildInterface, - FloorParentInterface, - GetFloorByUserUuidInterface, - GetFloorByUuidInterface, - RenameFloorByUuidInterface, -} from '../interface/floor.interface'; -import { SpaceEntity } from '@app/common/modules/space/entities'; -import { UpdateFloorNameDto } from '../dtos/update.floor.dto'; -import { UserSpaceRepository } from '@app/common/modules/user/repositories'; -import { SpaceType } from '@app/common/constants/space-type.enum'; -import { CommonErrorCodes } from '@app/common/constants/error-codes.enum'; - -@Injectable() -export class FloorService { - constructor( - private readonly spaceRepository: SpaceRepository, - private readonly spaceTypeRepository: SpaceTypeRepository, - private readonly userSpaceRepository: UserSpaceRepository, - ) {} - - async addFloor(addFloorDto: AddFloorDto) { - try { - const spaceType = await this.spaceTypeRepository.findOne({ - where: { - type: SpaceType.FLOOR, - }, - }); - - const floor = await this.spaceRepository.save({ - spaceName: addFloorDto.floorName, - parent: { uuid: addFloorDto.buildingUuid }, - spaceType: { uuid: spaceType.uuid }, - }); - return floor; - } catch (err) { - throw new HttpException(err.message, HttpStatus.INTERNAL_SERVER_ERROR); - } - } - - async getFloorByUuid(floorUuid: string): Promise { - try { - const floor = await this.spaceRepository.findOne({ - where: { - uuid: floorUuid, - spaceType: { - type: SpaceType.FLOOR, - }, - }, - relations: ['spaceType'], - }); - if ( - !floor || - !floor.spaceType || - floor.spaceType.type !== SpaceType.FLOOR - ) { - throw new BadRequestException('Invalid floor UUID'); - } - - return { - uuid: floor.uuid, - createdAt: floor.createdAt, - updatedAt: floor.updatedAt, - name: floor.spaceName, - type: floor.spaceType.type, - }; - } catch (err) { - if (err instanceof BadRequestException) { - throw err; // Re-throw BadRequestException - } else { - throw new HttpException('Floor not found', HttpStatus.NOT_FOUND); - } - } - } - async getFloorChildByUuid( - floorUuid: string, - getFloorChildDto: GetFloorChildDto, - ): Promise { - try { - const { includeSubSpaces, page, pageSize } = getFloorChildDto; - - const space = await this.spaceRepository.findOneOrFail({ - where: { uuid: floorUuid }, - relations: ['children', 'spaceType'], - }); - - if ( - !space || - !space.spaceType || - space.spaceType.type !== SpaceType.FLOOR - ) { - throw new BadRequestException('Invalid floor UUID'); - } - const totalCount = await this.spaceRepository.count({ - where: { parent: { uuid: space.uuid } }, - }); - - const children = await this.buildHierarchy( - space, - includeSubSpaces, - page, - pageSize, - ); - - return { - uuid: space.uuid, - name: space.spaceName, - type: space.spaceType.type, - totalCount, - children, - }; - } catch (err) { - if (err instanceof BadRequestException) { - throw err; // Re-throw BadRequestException - } else { - throw new HttpException('Floor not found', HttpStatus.NOT_FOUND); - } - } - } - - private async buildHierarchy( - space: SpaceEntity, - includeSubSpaces: boolean, - page: number, - pageSize: number, - ): Promise { - const children = await this.spaceRepository.find({ - where: { parent: { uuid: space.uuid } }, - relations: ['spaceType'], - skip: (page - 1) * pageSize, - take: pageSize, - }); - - if (!children || children.length === 0 || !includeSubSpaces) { - return children - .filter( - (child) => - child.spaceType.type !== SpaceType.FLOOR && - child.spaceType.type !== SpaceType.BUILDING && - child.spaceType.type !== SpaceType.COMMUNITY, - ) // Filter remaining floor and building and community types - .map((child) => ({ - uuid: child.uuid, - name: child.spaceName, - type: child.spaceType.type, - })); - } - - const childHierarchies = await Promise.all( - children - .filter( - (child) => - child.spaceType.type !== SpaceType.FLOOR && - child.spaceType.type !== SpaceType.BUILDING && - child.spaceType.type !== SpaceType.COMMUNITY, - ) // Filter remaining floor and building and community types - .map(async (child) => ({ - uuid: child.uuid, - name: child.spaceName, - type: child.spaceType.type, - children: await this.buildHierarchy(child, true, 1, pageSize), - })), - ); - - return childHierarchies; - } - - async getFloorParentByUuid(floorUuid: string): Promise { - try { - const floor = await this.spaceRepository.findOne({ - where: { - uuid: floorUuid, - spaceType: { - type: SpaceType.FLOOR, - }, - }, - relations: ['spaceType', 'parent', 'parent.spaceType'], - }); - if ( - !floor || - !floor.spaceType || - floor.spaceType.type !== SpaceType.FLOOR - ) { - throw new BadRequestException('Invalid floor UUID'); - } - - return { - uuid: floor.uuid, - name: floor.spaceName, - type: floor.spaceType.type, - parent: { - uuid: floor.parent.uuid, - name: floor.parent.spaceName, - type: floor.parent.spaceType.type, - }, - }; - } catch (err) { - if (err instanceof BadRequestException) { - throw err; // Re-throw BadRequestException - } else { - throw new HttpException('Floor not found', HttpStatus.NOT_FOUND); - } - } - } - - async getFloorsByUserId( - userUuid: string, - ): Promise { - try { - const floors = await this.userSpaceRepository.find({ - relations: ['space', 'space.spaceType'], - where: { - user: { uuid: userUuid }, - space: { spaceType: { type: SpaceType.FLOOR } }, - }, - }); - - if (floors.length === 0) { - throw new HttpException( - 'this user has no floors', - HttpStatus.NOT_FOUND, - ); - } - const spaces = floors.map((floor) => ({ - uuid: floor.space.uuid, - name: floor.space.spaceName, - type: floor.space.spaceType.type, - })); - - return spaces; - } catch (err) { - if (err instanceof HttpException) { - throw err; - } else { - throw new HttpException('user not found', HttpStatus.NOT_FOUND); - } - } - } - async addUserFloor(addUserFloorDto: AddUserFloorDto) { - try { - await this.userSpaceRepository.save({ - user: { uuid: addUserFloorDto.userUuid }, - space: { uuid: addUserFloorDto.floorUuid }, - }); - } catch (err) { - if (err.code === CommonErrorCodes.DUPLICATE_ENTITY) { - throw new HttpException( - 'User already belongs to this floor', - HttpStatus.BAD_REQUEST, - ); - } - throw new HttpException( - err.message || 'Internal Server Error', - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } - async renameFloorByUuid( - floorUuid: string, - updateFloorDto: UpdateFloorNameDto, - ): Promise { - try { - const floor = await this.spaceRepository.findOneOrFail({ - where: { uuid: floorUuid }, - relations: ['spaceType'], - }); - - if ( - !floor || - !floor.spaceType || - floor.spaceType.type !== SpaceType.FLOOR - ) { - throw new BadRequestException('Invalid floor UUID'); - } - - await this.spaceRepository.update( - { uuid: floorUuid }, - { spaceName: updateFloorDto.floorName }, - ); - - // Fetch the updated floor - const updatedFloor = await this.spaceRepository.findOneOrFail({ - where: { uuid: floorUuid }, - relations: ['spaceType'], - }); - - return { - uuid: updatedFloor.uuid, - name: updatedFloor.spaceName, - type: updatedFloor.spaceType.type, - }; - } catch (err) { - if (err instanceof BadRequestException) { - throw err; // Re-throw BadRequestException - } else { - throw new HttpException('Floor not found', HttpStatus.NOT_FOUND); - } - } - } -} diff --git a/src/floor/services/index.ts b/src/floor/services/index.ts deleted file mode 100644 index e6f7946..0000000 --- a/src/floor/services/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './floor.service'; diff --git a/src/guards/building.type.guard.ts b/src/guards/building.type.guard.ts index dd2870e..29fba8a 100644 --- a/src/guards/building.type.guard.ts +++ b/src/guards/building.type.guard.ts @@ -1,4 +1,3 @@ -import { SpaceType } from '@app/common/constants/space-type.enum'; import { SpaceRepository } from '@app/common/modules/space/repositories'; import { Injectable, @@ -40,11 +39,7 @@ export class CheckBuildingTypeGuard implements CanActivate { where: { uuid: buildingUuid }, relations: ['spaceType'], }); - if ( - !buildingData || - !buildingData.spaceType || - buildingData.spaceType.type !== SpaceType.BUILDING - ) { + if (!buildingData) { throw new BadRequestException('Invalid building UUID'); } } diff --git a/src/guards/community.type.guard.ts b/src/guards/community.type.guard.ts index 9dc5d01..f001978 100644 --- a/src/guards/community.type.guard.ts +++ b/src/guards/community.type.guard.ts @@ -6,7 +6,6 @@ import { } from '@nestjs/common'; import { SpaceRepository } from '@app/common/modules/space/repositories'; import { BadRequestException } from '@nestjs/common'; -import { SpaceType } from '@app/common/constants/space-type.enum'; @Injectable() export class CheckCommunityTypeGuard implements CanActivate { @@ -41,11 +40,7 @@ export class CheckCommunityTypeGuard implements CanActivate { relations: ['spaceType'], }); - if ( - !communityData || - !communityData.spaceType || - communityData.spaceType.type !== SpaceType.COMMUNITY - ) { + if (!communityData) { throw new BadRequestException('Invalid community UUID'); } } diff --git a/src/guards/floor.type.guard.ts b/src/guards/floor.type.guard.ts index 3e6b875..b7b7215 100644 --- a/src/guards/floor.type.guard.ts +++ b/src/guards/floor.type.guard.ts @@ -1,4 +1,3 @@ -import { SpaceType } from '@app/common/constants/space-type.enum'; import { SpaceRepository } from '@app/common/modules/space/repositories'; import { Injectable, @@ -37,14 +36,9 @@ export class CheckFloorTypeGuard implements CanActivate { async checkFloorIsFloorType(floorUuid: string) { const floorData = await this.spaceRepository.findOne({ where: { uuid: floorUuid }, - relations: ['spaceType'], }); - if ( - !floorData || - !floorData.spaceType || - floorData.spaceType.type !== SpaceType.FLOOR - ) { + if (!floorData) { throw new BadRequestException('Invalid floor UUID'); } } diff --git a/src/guards/room.guard.ts b/src/guards/room.guard.ts index bd63520..e8d0550 100644 --- a/src/guards/room.guard.ts +++ b/src/guards/room.guard.ts @@ -8,7 +8,6 @@ import { import { SpaceRepository } from '@app/common/modules/space/repositories'; import { BadRequestException, NotFoundException } from '@nestjs/common'; import { DeviceRepository } from '@app/common/modules/device/repositories'; -import { SpaceType } from '@app/common/constants/space-type.enum'; @Injectable() export class CheckRoomGuard implements CanActivate { @@ -43,9 +42,6 @@ export class CheckRoomGuard implements CanActivate { const room = await this.spaceRepository.findOne({ where: { uuid: roomUuid, - spaceType: { - type: SpaceType.ROOM, - }, }, }); if (!room) { diff --git a/src/guards/unit.type.guard.ts b/src/guards/unit.type.guard.ts index a753756..d1fa50b 100644 --- a/src/guards/unit.type.guard.ts +++ b/src/guards/unit.type.guard.ts @@ -1,4 +1,3 @@ -import { SpaceType } from '@app/common/constants/space-type.enum'; import { SpaceRepository } from '@app/common/modules/space/repositories'; import { Injectable, @@ -37,14 +36,9 @@ export class CheckUnitTypeGuard implements CanActivate { async checkFloorIsFloorType(unitUuid: string) { const unitData = await this.spaceRepository.findOne({ where: { uuid: unitUuid }, - relations: ['spaceType'], }); - if ( - !unitData || - !unitData.spaceType || - unitData.spaceType.type !== SpaceType.UNIT - ) { + if (!unitData) { throw new BadRequestException('Invalid unit UUID'); } } diff --git a/src/guards/user.building.guard.ts b/src/guards/user.building.guard.ts index 3f74500..aa23d89 100644 --- a/src/guards/user.building.guard.ts +++ b/src/guards/user.building.guard.ts @@ -7,7 +7,6 @@ import { import { SpaceRepository } from '@app/common/modules/space/repositories'; import { BadRequestException, NotFoundException } from '@nestjs/common'; import { UserRepository } from '@app/common/modules/user/repositories'; -import { SpaceType } from '@app/common/constants/space-type.enum'; @Injectable() export class CheckUserBuildingGuard implements CanActivate { @@ -44,7 +43,7 @@ export class CheckUserBuildingGuard implements CanActivate { private async checkBuildingIsFound(spaceUuid: string) { const spaceData = await this.spaceRepository.findOne({ - where: { uuid: spaceUuid, spaceType: { type: SpaceType.BUILDING } }, + where: { uuid: spaceUuid }, relations: ['spaceType'], }); if (!spaceData) { diff --git a/src/guards/user.community.guard.ts b/src/guards/user.community.guard.ts index e8dea71..d3c1e5a 100644 --- a/src/guards/user.community.guard.ts +++ b/src/guards/user.community.guard.ts @@ -7,7 +7,6 @@ import { import { SpaceRepository } from '@app/common/modules/space/repositories'; import { BadRequestException, NotFoundException } from '@nestjs/common'; import { UserRepository } from '@app/common/modules/user/repositories'; -import { SpaceType } from '@app/common/constants/space-type.enum'; @Injectable() export class CheckUserCommunityGuard implements CanActivate { @@ -44,8 +43,7 @@ export class CheckUserCommunityGuard implements CanActivate { private async checkCommunityIsFound(spaceUuid: string) { const spaceData = await this.spaceRepository.findOne({ - where: { uuid: spaceUuid, spaceType: { type: SpaceType.COMMUNITY } }, - relations: ['spaceType'], + where: { uuid: spaceUuid }, }); if (!spaceData) { throw new NotFoundException('Community not found'); diff --git a/src/guards/user.floor.guard.ts b/src/guards/user.floor.guard.ts index 6faa520..3c6e45e 100644 --- a/src/guards/user.floor.guard.ts +++ b/src/guards/user.floor.guard.ts @@ -44,8 +44,7 @@ export class CheckUserFloorGuard implements CanActivate { private async checkFloorIsFound(spaceUuid: string) { const spaceData = await this.spaceRepository.findOne({ - where: { uuid: spaceUuid, spaceType: { type: SpaceType.FLOOR } }, - relations: ['spaceType'], + where: { uuid: spaceUuid }, }); if (!spaceData) { throw new NotFoundException('Floor not found'); diff --git a/src/guards/user.room.guard.ts b/src/guards/user.room.guard.ts index 49c77b8..9f38712 100644 --- a/src/guards/user.room.guard.ts +++ b/src/guards/user.room.guard.ts @@ -7,7 +7,6 @@ import { import { SpaceRepository } from '@app/common/modules/space/repositories'; import { BadRequestException, NotFoundException } from '@nestjs/common'; import { UserRepository } from '@app/common/modules/user/repositories'; -import { SpaceType } from '@app/common/constants/space-type.enum'; @Injectable() export class CheckUserRoomGuard implements CanActivate { @@ -44,7 +43,7 @@ export class CheckUserRoomGuard implements CanActivate { private async checkRoomIsFound(spaceUuid: string) { const spaceData = await this.spaceRepository.findOne({ - where: { uuid: spaceUuid, spaceType: { type: SpaceType.ROOM } }, + where: { uuid: spaceUuid }, relations: ['spaceType'], }); if (!spaceData) { diff --git a/src/guards/user.unit.guard.ts b/src/guards/user.unit.guard.ts index eb60a27..f5f1bfb 100644 --- a/src/guards/user.unit.guard.ts +++ b/src/guards/user.unit.guard.ts @@ -7,7 +7,6 @@ import { import { SpaceRepository } from '@app/common/modules/space/repositories'; import { BadRequestException, NotFoundException } from '@nestjs/common'; import { UserRepository } from '@app/common/modules/user/repositories'; -import { SpaceType } from '@app/common/constants/space-type.enum'; @Injectable() export class CheckUserUnitGuard implements CanActivate { @@ -44,7 +43,7 @@ export class CheckUserUnitGuard implements CanActivate { private async checkUnitIsFound(spaceUuid: string) { const spaceData = await this.spaceRepository.findOne({ - where: { uuid: spaceUuid, spaceType: { type: SpaceType.UNIT } }, + where: { uuid: spaceUuid }, relations: ['spaceType'], }); if (!spaceData) { diff --git a/src/room/controllers/index.ts b/src/room/controllers/index.ts deleted file mode 100644 index 4225d61..0000000 --- a/src/room/controllers/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './room.controller'; diff --git a/src/room/controllers/room.controller.ts b/src/room/controllers/room.controller.ts deleted file mode 100644 index e3eb687..0000000 --- a/src/room/controllers/room.controller.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { RoomService } from '../services/room.service'; -import { - Body, - Controller, - Get, - HttpStatus, - Param, - Post, - Put, - UseGuards, -} from '@nestjs/common'; -import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; -import { AddRoomDto, AddUserRoomDto } from '../dtos/add.room.dto'; -import { UpdateRoomNameDto } from '../dtos/update.room.dto'; -import { CheckUnitTypeGuard } from 'src/guards/unit.type.guard'; -import { CheckUserRoomGuard } from 'src/guards/user.room.guard'; -import { AdminRoleGuard } from 'src/guards/admin.role.guard'; -import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; -import { RoomPermissionGuard } from 'src/guards/room.permission.guard'; -import { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; -import { SpaceType } from '@app/common/constants/space-type.enum'; - -@ApiTags('Room Module') -@Controller({ - version: EnableDisableStatusEnum.ENABLED, - path: SpaceType.ROOM, -}) -export class RoomController { - constructor(private readonly roomService: RoomService) {} - - @ApiBearerAuth() - @UseGuards(JwtAuthGuard, CheckUnitTypeGuard) - @Post() - async addRoom(@Body() addRoomDto: AddRoomDto) { - const room = await this.roomService.addRoom(addRoomDto); - return { - statusCode: HttpStatus.CREATED, - success: true, - message: 'Room added successfully', - data: room, - }; - } - - @ApiBearerAuth() - @UseGuards(JwtAuthGuard, RoomPermissionGuard) - @Get(':roomUuid') - async getRoomByUuid(@Param('roomUuid') roomUuid: string) { - const room = await this.roomService.getRoomByUuid(roomUuid); - return room; - } - - @ApiBearerAuth() - @UseGuards(JwtAuthGuard, RoomPermissionGuard) - @Get('parent/:roomUuid') - async getRoomParentByUuid(@Param('roomUuid') roomUuid: string) { - const room = await this.roomService.getRoomParentByUuid(roomUuid); - return room; - } - @ApiBearerAuth() - @UseGuards(AdminRoleGuard, CheckUserRoomGuard) - @Post('user') - async addUserRoom(@Body() addUserRoomDto: AddUserRoomDto) { - await this.roomService.addUserRoom(addUserRoomDto); - return { - statusCode: HttpStatus.CREATED, - success: true, - message: 'user room added successfully', - }; - } - @ApiBearerAuth() - @UseGuards(JwtAuthGuard) - @Get('user/:userUuid') - async getRoomsByUserId(@Param('userUuid') userUuid: string) { - return await this.roomService.getRoomsByUserId(userUuid); - } - - @ApiBearerAuth() - @UseGuards(JwtAuthGuard, RoomPermissionGuard) - @Put(':roomUuid') - async renameRoomByUuid( - @Param('roomUuid') roomUuid: string, - @Body() updateRoomNameDto: UpdateRoomNameDto, - ) { - const room = await this.roomService.renameRoomByUuid( - roomUuid, - updateRoomNameDto, - ); - return room; - } -} diff --git a/src/room/dtos/add.room.dto.ts b/src/room/dtos/add.room.dto.ts deleted file mode 100644 index 2718a29..0000000 --- a/src/room/dtos/add.room.dto.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsNotEmpty, IsString } from 'class-validator'; - -export class AddRoomDto { - @ApiProperty({ - description: 'roomName', - required: true, - }) - @IsString() - @IsNotEmpty() - public roomName: string; - - @ApiProperty({ - description: 'unitUuid', - required: true, - }) - @IsString() - @IsNotEmpty() - public unitUuid: string; - constructor(dto: Partial) { - Object.assign(this, dto); - } -} -export class AddUserRoomDto { - @ApiProperty({ - description: 'roomUuid', - required: true, - }) - @IsString() - @IsNotEmpty() - public roomUuid: string; - @ApiProperty({ - description: 'userUuid', - required: true, - }) - @IsString() - @IsNotEmpty() - public userUuid: string; - constructor(dto: Partial) { - Object.assign(this, dto); - } -} diff --git a/src/room/dtos/index.ts b/src/room/dtos/index.ts deleted file mode 100644 index a510b75..0000000 --- a/src/room/dtos/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './add.room.dto'; diff --git a/src/room/dtos/update.room.dto.ts b/src/room/dtos/update.room.dto.ts deleted file mode 100644 index 8f54092..0000000 --- a/src/room/dtos/update.room.dto.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsNotEmpty, IsString } from 'class-validator'; - -export class UpdateRoomNameDto { - @ApiProperty({ - description: 'roomName', - required: true, - }) - @IsString() - @IsNotEmpty() - public roomName: string; - - constructor(dto: Partial) { - Object.assign(this, dto); - } -} diff --git a/src/room/interface/room.interface.ts b/src/room/interface/room.interface.ts deleted file mode 100644 index 49473a3..0000000 --- a/src/room/interface/room.interface.ts +++ /dev/null @@ -1,24 +0,0 @@ -export interface GetRoomByUuidInterface { - uuid: string; - createdAt: Date; - updatedAt: Date; - name: string; - type: string; -} - -export interface RoomParentInterface { - uuid: string; - name: string; - type: string; - parent?: RoomParentInterface; -} -export interface RenameRoomByUuidInterface { - uuid: string; - name: string; - type: string; -} -export interface GetRoomByUserUuidInterface { - uuid: string; - name: string; - type: string; -} diff --git a/src/room/room.module.ts b/src/room/room.module.ts deleted file mode 100644 index 4a07d1a..0000000 --- a/src/room/room.module.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Module } from '@nestjs/common'; -import { RoomService } from './services/room.service'; -import { RoomController } from './controllers/room.controller'; -import { ConfigModule } from '@nestjs/config'; -import { SpaceRepositoryModule } from '@app/common/modules/space/space.repository.module'; -import { SpaceRepository } from '@app/common/modules/space/repositories'; -import { SpaceTypeRepository } from '@app/common/modules/space/repositories'; -import { UserSpaceRepository } from '@app/common/modules/user/repositories'; -import { UserRepositoryModule } from '@app/common/modules/user/user.repository.module'; -import { UserRepository } from '@app/common/modules/user/repositories'; - -@Module({ - imports: [ConfigModule, SpaceRepositoryModule, UserRepositoryModule], - controllers: [RoomController], - providers: [ - RoomService, - SpaceRepository, - SpaceTypeRepository, - UserSpaceRepository, - UserRepository, - ], - exports: [RoomService], -}) -export class RoomModule {} diff --git a/src/room/services/index.ts b/src/room/services/index.ts deleted file mode 100644 index 4f45e9a..0000000 --- a/src/room/services/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './room.service'; diff --git a/src/room/services/room.service.ts b/src/room/services/room.service.ts deleted file mode 100644 index 340df0f..0000000 --- a/src/room/services/room.service.ts +++ /dev/null @@ -1,200 +0,0 @@ -import { SpaceTypeRepository } from '../../../libs/common/src/modules/space/repositories/space.repository'; -import { - Injectable, - HttpException, - HttpStatus, - BadRequestException, -} from '@nestjs/common'; -import { SpaceRepository } from '@app/common/modules/space/repositories'; -import { AddRoomDto, AddUserRoomDto } from '../dtos'; -import { - RoomParentInterface, - GetRoomByUuidInterface, - RenameRoomByUuidInterface, - GetRoomByUserUuidInterface, -} from '../interface/room.interface'; -import { UpdateRoomNameDto } from '../dtos/update.room.dto'; -import { UserSpaceRepository } from '@app/common/modules/user/repositories'; -import { SpaceType } from '@app/common/constants/space-type.enum'; -import { CommonErrorCodes } from '@app/common/constants/error-codes.enum'; - -@Injectable() -export class RoomService { - constructor( - private readonly spaceRepository: SpaceRepository, - private readonly spaceTypeRepository: SpaceTypeRepository, - private readonly userSpaceRepository: UserSpaceRepository, - ) {} - - async addRoom(addRoomDto: AddRoomDto) { - try { - const spaceType = await this.spaceTypeRepository.findOne({ - where: { - type: SpaceType.ROOM, - }, - }); - - const room = await this.spaceRepository.save({ - spaceName: addRoomDto.roomName, - parent: { uuid: addRoomDto.unitUuid }, - spaceType: { uuid: spaceType.uuid }, - }); - return room; - } catch (err) { - throw new HttpException(err.message, HttpStatus.INTERNAL_SERVER_ERROR); - } - } - - async getRoomByUuid(roomUuid: string): Promise { - try { - const room = await this.spaceRepository.findOne({ - where: { - uuid: roomUuid, - spaceType: { - type: SpaceType.ROOM, - }, - }, - relations: ['spaceType'], - }); - if (!room || !room.spaceType || room.spaceType.type !== SpaceType.ROOM) { - throw new BadRequestException('Invalid room UUID'); - } - - return { - uuid: room.uuid, - createdAt: room.createdAt, - updatedAt: room.updatedAt, - name: room.spaceName, - type: room.spaceType.type, - }; - } catch (err) { - if (err instanceof BadRequestException) { - throw err; // Re-throw BadRequestException - } else { - throw new HttpException('Room not found', HttpStatus.NOT_FOUND); - } - } - } - - async getRoomParentByUuid(roomUuid: string): Promise { - try { - const room = await this.spaceRepository.findOne({ - where: { - uuid: roomUuid, - spaceType: { - type: SpaceType.ROOM, - }, - }, - relations: ['spaceType', 'parent', 'parent.spaceType'], - }); - if (!room || !room.spaceType || room.spaceType.type !== SpaceType.ROOM) { - throw new BadRequestException('Invalid room UUID'); - } - - return { - uuid: room.uuid, - name: room.spaceName, - type: room.spaceType.type, - parent: { - uuid: room.parent.uuid, - name: room.parent.spaceName, - type: room.parent.spaceType.type, - }, - }; - } catch (err) { - if (err instanceof BadRequestException) { - throw err; // Re-throw BadRequestException - } else { - throw new HttpException('Room not found', HttpStatus.NOT_FOUND); - } - } - } - - async getRoomsByUserId( - userUuid: string, - ): Promise { - try { - const rooms = await this.userSpaceRepository.find({ - relations: ['space', 'space.spaceType'], - where: { - user: { uuid: userUuid }, - space: { spaceType: { type: SpaceType.ROOM } }, - }, - }); - - if (rooms.length === 0) { - throw new HttpException('this user has no rooms', HttpStatus.NOT_FOUND); - } - const spaces = rooms.map((room) => ({ - uuid: room.space.uuid, - name: room.space.spaceName, - type: room.space.spaceType.type, - })); - - return spaces; - } catch (err) { - if (err instanceof HttpException) { - throw err; - } else { - throw new HttpException('user not found', HttpStatus.NOT_FOUND); - } - } - } - async addUserRoom(addUserRoomDto: AddUserRoomDto) { - try { - await this.userSpaceRepository.save({ - user: { uuid: addUserRoomDto.userUuid }, - space: { uuid: addUserRoomDto.roomUuid }, - }); - } catch (err) { - if (err.code === CommonErrorCodes.DUPLICATE_ENTITY) { - throw new HttpException( - 'User already belongs to this room', - HttpStatus.BAD_REQUEST, - ); - } - throw new HttpException( - err.message || 'Internal Server Error', - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } - async renameRoomByUuid( - roomUuid: string, - updateRoomNameDto: UpdateRoomNameDto, - ): Promise { - try { - const room = await this.spaceRepository.findOneOrFail({ - where: { uuid: roomUuid }, - relations: ['spaceType'], - }); - - if (!room || !room.spaceType || room.spaceType.type !== SpaceType.ROOM) { - throw new BadRequestException('Invalid room UUID'); - } - - await this.spaceRepository.update( - { uuid: roomUuid }, - { spaceName: updateRoomNameDto.roomName }, - ); - - // Fetch the updated room - const updateRoom = await this.spaceRepository.findOneOrFail({ - where: { uuid: roomUuid }, - relations: ['spaceType'], - }); - - return { - uuid: updateRoom.uuid, - name: updateRoom.spaceName, - type: updateRoom.spaceType.type, - }; - } catch (err) { - if (err instanceof BadRequestException) { - throw err; // Re-throw BadRequestException - } else { - throw new HttpException('Room not found', HttpStatus.NOT_FOUND); - } - } - } -} diff --git a/src/scene/services/scene.service.ts b/src/scene/services/scene.service.ts index 30e53aa..5615011 100644 --- a/src/scene/services/scene.service.ts +++ b/src/scene/services/scene.service.ts @@ -6,7 +6,6 @@ import { } from '@nestjs/common'; import { SpaceRepository } from '@app/common/modules/space/repositories'; import { AddSceneTapToRunDto, UpdateSceneTapToRunDto } from '../dtos'; -import { GetUnitByUuidInterface } from 'src/unit/interface/unit.interface'; import { ConfigService } from '@nestjs/config'; import { TuyaContext } from '@tuya/tuya-connector-nodejs'; import { convertKeysToSnakeCase } from '@app/common/helper/snakeCaseConverter'; @@ -18,7 +17,6 @@ import { SceneDetailsResult, } from '../interface/scene.interface'; import { convertKeysToCamelCase } from '@app/common/helper/camelCaseConverter'; -import { SpaceType } from '@app/common/constants/space-type.enum'; import { ActionExecutorEnum } from '@app/common/constants/automation.enum'; @Injectable() @@ -104,18 +102,14 @@ export class SceneService { } } } - async getUnitByUuid(unitUuid: string): Promise { + async getUnitByUuid(unitUuid: string) { try { const unit = await this.spaceRepository.findOne({ where: { uuid: unitUuid, - spaceType: { - type: SpaceType.UNIT, - }, }, - relations: ['spaceType'], }); - if (!unit || !unit.spaceType || unit.spaceType.type !== SpaceType.UNIT) { + if (!unit) { throw new BadRequestException('Invalid unit UUID'); } return { @@ -123,7 +117,6 @@ export class SceneService { createdAt: unit.createdAt, updatedAt: unit.updatedAt, name: unit.spaceName, - type: unit.spaceType.type, spaceTuyaUuid: unit.spaceTuyaUuid, }; } catch (err) { diff --git a/src/unit/controllers/index.ts b/src/unit/controllers/index.ts deleted file mode 100644 index c8d7271..0000000 --- a/src/unit/controllers/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './unit.controller'; diff --git a/src/unit/controllers/unit.controller.ts b/src/unit/controllers/unit.controller.ts deleted file mode 100644 index 8dbede7..0000000 --- a/src/unit/controllers/unit.controller.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { UnitService } from '../services/unit.service'; -import { - Body, - Controller, - Get, - HttpStatus, - Param, - Post, - Put, - Query, - UseGuards, -} from '@nestjs/common'; -import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; -import { - AddUnitDto, - AddUserUnitDto, - AddUserUnitUsingCodeDto, -} from '../dtos/add.unit.dto'; -import { GetUnitChildDto } from '../dtos/get.unit.dto'; -import { UpdateUnitNameDto } from '../dtos/update.unit.dto'; -import { CheckFloorTypeGuard } from 'src/guards/floor.type.guard'; -import { CheckUserUnitGuard } from 'src/guards/user.unit.guard'; -import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; -import { UnitPermissionGuard } from 'src/guards/unit.permission.guard'; -import { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; -import { SpaceType } from '@app/common/constants/space-type.enum'; - -@ApiTags('Unit Module') -@Controller({ - version: EnableDisableStatusEnum.ENABLED, - path: SpaceType.UNIT, -}) -export class UnitController { - constructor(private readonly unitService: UnitService) {} - - @ApiBearerAuth() - @UseGuards(JwtAuthGuard, CheckFloorTypeGuard) - @Post() - async addUnit(@Body() addUnitDto: AddUnitDto) { - const unit = await this.unitService.addUnit(addUnitDto); - return { - statusCode: HttpStatus.CREATED, - success: true, - message: 'Unit added successfully', - data: unit, - }; - } - - @ApiBearerAuth() - @UseGuards(JwtAuthGuard, UnitPermissionGuard) - @Get(':unitUuid') - async getUnitByUuid(@Param('unitUuid') unitUuid: string) { - const unit = await this.unitService.getUnitByUuid(unitUuid); - return unit; - } - - @ApiBearerAuth() - @UseGuards(JwtAuthGuard, UnitPermissionGuard) - @Get('child/:unitUuid') - async getUnitChildByUuid( - @Param('unitUuid') unitUuid: string, - @Query() query: GetUnitChildDto, - ) { - const unit = await this.unitService.getUnitChildByUuid(unitUuid, query); - return unit; - } - @ApiBearerAuth() - @UseGuards(JwtAuthGuard, UnitPermissionGuard) - @Get('parent/:unitUuid') - async getUnitParentByUuid(@Param('unitUuid') unitUuid: string) { - const unit = await this.unitService.getUnitParentByUuid(unitUuid); - return unit; - } - @ApiBearerAuth() - @UseGuards(JwtAuthGuard, CheckUserUnitGuard) - @Post('user') - async addUserUnit(@Body() addUserUnitDto: AddUserUnitDto) { - await this.unitService.addUserUnit(addUserUnitDto); - return { - statusCode: HttpStatus.CREATED, - success: true, - message: 'user unit added successfully', - }; - } - @ApiBearerAuth() - @UseGuards(JwtAuthGuard) - @Get('user/:userUuid') - async getUnitsByUserId(@Param('userUuid') userUuid: string) { - return await this.unitService.getUnitsByUserId(userUuid); - } - - @ApiBearerAuth() - @UseGuards(JwtAuthGuard, UnitPermissionGuard) - @Put(':unitUuid') - async renameUnitByUuid( - @Param('unitUuid') unitUuid: string, - @Body() updateUnitNameDto: UpdateUnitNameDto, - ) { - const unit = await this.unitService.renameUnitByUuid( - unitUuid, - updateUnitNameDto, - ); - return unit; - } - @ApiBearerAuth() - @UseGuards(JwtAuthGuard, UnitPermissionGuard) - @Get(':unitUuid/invitation-code') - async getUnitInvitationCode(@Param('unitUuid') unitUuid: string) { - const unit = await this.unitService.getUnitInvitationCode(unitUuid); - return unit; - } - - @ApiBearerAuth() - @UseGuards(JwtAuthGuard) - @Post('user/verify-code') - async verifyCodeAndAddUserUnit( - @Body() addUserUnitUsingCodeDto: AddUserUnitUsingCodeDto, - ) { - await this.unitService.verifyCodeAndAddUserUnit(addUserUnitUsingCodeDto); - return { - statusCode: HttpStatus.CREATED, - success: true, - message: 'user unit added successfully', - }; - } -} diff --git a/src/unit/dtos/add.unit.dto.ts b/src/unit/dtos/add.unit.dto.ts deleted file mode 100644 index 9896c37..0000000 --- a/src/unit/dtos/add.unit.dto.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsNotEmpty, IsString } from 'class-validator'; - -export class AddUnitDto { - @ApiProperty({ - description: 'unitName', - required: true, - }) - @IsString() - @IsNotEmpty() - public unitName: string; - - @ApiProperty({ - description: 'floorUuid', - required: true, - }) - @IsString() - @IsNotEmpty() - public floorUuid: string; - constructor(dto: Partial) { - Object.assign(this, dto); - } -} -export class AddUserUnitDto { - @ApiProperty({ - description: 'unitUuid', - required: true, - }) - @IsString() - @IsNotEmpty() - public unitUuid: string; - @ApiProperty({ - description: 'userUuid', - required: true, - }) - @IsString() - @IsNotEmpty() - public userUuid: string; - constructor(dto: Partial) { - Object.assign(this, dto); - } -} -export class AddUserUnitUsingCodeDto { - @ApiProperty({ - description: 'userUuid', - required: true, - }) - @IsString() - @IsNotEmpty() - public userUuid: string; - @ApiProperty({ - description: 'inviteCode', - required: true, - }) - @IsString() - @IsNotEmpty() - public inviteCode: string; - constructor(dto: Partial) { - Object.assign(this, dto); - } -} diff --git a/src/unit/dtos/get.unit.dto.ts b/src/unit/dtos/get.unit.dto.ts deleted file mode 100644 index 2fae52a..0000000 --- a/src/unit/dtos/get.unit.dto.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsInt, IsNotEmpty, IsString, Min } from 'class-validator'; - -export class GetUnitDto { - @ApiProperty({ - description: 'unitUuid', - required: true, - }) - @IsString() - @IsNotEmpty() - public unitUuid: string; -} - -export class GetUnitChildDto { - @ApiProperty({ example: 1, description: 'Page number', required: true }) - @IsInt({ message: 'Page must be a number' }) - @Min(1, { message: 'Page must not be less than 1' }) - @IsNotEmpty() - public page: number; - - @ApiProperty({ - example: 10, - description: 'Number of items per page', - required: true, - }) - @IsInt({ message: 'Page size must be a number' }) - @Min(1, { message: 'Page size must not be less than 1' }) - @IsNotEmpty() - public pageSize: number; -} -export class GetUnitByUserIdDto { - @ApiProperty({ - description: 'userUuid', - required: true, - }) - @IsString() - @IsNotEmpty() - public userUuid: string; -} diff --git a/src/unit/dtos/index.ts b/src/unit/dtos/index.ts deleted file mode 100644 index 970d13d..0000000 --- a/src/unit/dtos/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './add.unit.dto'; diff --git a/src/unit/dtos/update.unit.dto.ts b/src/unit/dtos/update.unit.dto.ts deleted file mode 100644 index 2d69902..0000000 --- a/src/unit/dtos/update.unit.dto.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsNotEmpty, IsString } from 'class-validator'; - -export class UpdateUnitNameDto { - @ApiProperty({ - description: 'unitName', - required: true, - }) - @IsString() - @IsNotEmpty() - public unitName: string; - - constructor(dto: Partial) { - Object.assign(this, dto); - } -} diff --git a/src/unit/interface/unit.interface.ts b/src/unit/interface/unit.interface.ts deleted file mode 100644 index 635f38e..0000000 --- a/src/unit/interface/unit.interface.ts +++ /dev/null @@ -1,37 +0,0 @@ -export interface GetUnitByUuidInterface { - uuid: string; - createdAt: Date; - updatedAt: Date; - name: string; - type: string; - spaceTuyaUuid: string; -} - -export interface UnitChildInterface { - uuid: string; - name: string; - type: string; - totalCount?: number; - children?: UnitChildInterface[]; -} -export interface UnitParentInterface { - uuid: string; - name: string; - type: string; - parent?: UnitParentInterface; -} -export interface RenameUnitByUuidInterface { - uuid: string; - name: string; - type: string; -} -export interface GetUnitByUserUuidInterface { - uuid: string; - name: string; - type: string; -} -export interface addTuyaSpaceInterface { - success: boolean; - result: string; - msg: string; -} diff --git a/src/unit/services/index.ts b/src/unit/services/index.ts deleted file mode 100644 index 0540c40..0000000 --- a/src/unit/services/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './unit.service'; diff --git a/src/unit/services/unit.service.ts b/src/unit/services/unit.service.ts deleted file mode 100644 index 826c044..0000000 --- a/src/unit/services/unit.service.ts +++ /dev/null @@ -1,453 +0,0 @@ -import { GetUnitChildDto } from '../dtos/get.unit.dto'; -import { SpaceTypeRepository } from '../../../libs/common/src/modules/space/repositories/space.repository'; -import { - Injectable, - HttpException, - HttpStatus, - BadRequestException, -} from '@nestjs/common'; -import { SpaceRepository } from '@app/common/modules/space/repositories'; -import { AddUnitDto, AddUserUnitDto, AddUserUnitUsingCodeDto } from '../dtos'; -import { - UnitChildInterface, - UnitParentInterface, - GetUnitByUuidInterface, - RenameUnitByUuidInterface, - GetUnitByUserUuidInterface, - addTuyaSpaceInterface, -} from '../interface/unit.interface'; -import { SpaceEntity } from '@app/common/modules/space/entities'; -import { UpdateUnitNameDto } from '../dtos/update.unit.dto'; -import { UserSpaceRepository } from '@app/common/modules/user/repositories'; -import { generateRandomString } from '@app/common/helper/randomString'; -import { UserDevicePermissionService } from 'src/user-device-permission/services'; -import { PermissionType } from '@app/common/constants/permission-type.enum'; -import { TuyaContext } from '@tuya/tuya-connector-nodejs'; -import { ConfigService } from '@nestjs/config'; -import { SpaceType } from '@app/common/constants/space-type.enum'; -import { CommonErrorCodes } from '@app/common/constants/error-codes.enum'; - -@Injectable() -export class UnitService { - private tuya: TuyaContext; - constructor( - private readonly configService: ConfigService, - private readonly spaceRepository: SpaceRepository, - private readonly spaceTypeRepository: SpaceTypeRepository, - private readonly userSpaceRepository: UserSpaceRepository, - private readonly userDevicePermissionService: UserDevicePermissionService, - ) { - const accessKey = this.configService.get('auth-config.ACCESS_KEY'); - const secretKey = this.configService.get('auth-config.SECRET_KEY'); - const tuyaEuUrl = this.configService.get('tuya-config.TUYA_EU_URL'); - - this.tuya = new TuyaContext({ - baseUrl: tuyaEuUrl, - accessKey, - secretKey, - }); - } - - async addUnit(addUnitDto: AddUnitDto) { - try { - const spaceType = await this.spaceTypeRepository.findOne({ - where: { - type: SpaceType.UNIT, - }, - }); - const tuyaUnit = await this.addUnitTuya(addUnitDto.unitName); - if (!tuyaUnit.result) { - throw new HttpException('Error creating unit', HttpStatus.BAD_REQUEST); - } - - const unit = await this.spaceRepository.save({ - spaceName: addUnitDto.unitName, - parent: { uuid: addUnitDto.floorUuid }, - spaceType: { uuid: spaceType.uuid }, - spaceTuyaUuid: tuyaUnit.result, - }); - return unit; - } catch (err) { - throw new HttpException(err.message, HttpStatus.INTERNAL_SERVER_ERROR); - } - } - async addUnitTuya(unitName: string): Promise { - try { - const path = `/v2.0/cloud/space/creation`; - const response = await this.tuya.request({ - method: 'POST', - path, - body: { - name: unitName, - }, - }); - - return response as addTuyaSpaceInterface; - } catch (error) { - throw new HttpException( - 'Error creating unit from Tuya', - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } - - async getUnitByUuid(unitUuid: string): Promise { - try { - const unit = await this.spaceRepository.findOne({ - where: { - uuid: unitUuid, - spaceType: { - type: SpaceType.UNIT, - }, - }, - relations: ['spaceType'], - }); - if (!unit || !unit.spaceType || unit.spaceType.type !== SpaceType.UNIT) { - throw new BadRequestException('Invalid unit UUID'); - } - return { - uuid: unit.uuid, - createdAt: unit.createdAt, - updatedAt: unit.updatedAt, - name: unit.spaceName, - type: unit.spaceType.type, - spaceTuyaUuid: unit.spaceTuyaUuid, - }; - } catch (err) { - if (err instanceof BadRequestException) { - throw err; // Re-throw BadRequestException - } else { - throw new HttpException('Unit not found', HttpStatus.NOT_FOUND); - } - } - } - async getUnitChildByUuid( - unitUuid: string, - getUnitChildDto: GetUnitChildDto, - ): Promise { - try { - const { page, pageSize } = getUnitChildDto; - - const space = await this.spaceRepository.findOneOrFail({ - where: { uuid: unitUuid }, - relations: ['children', 'spaceType'], - }); - - if ( - !space || - !space.spaceType || - space.spaceType.type !== SpaceType.UNIT - ) { - throw new BadRequestException('Invalid unit UUID'); - } - - const totalCount = await this.spaceRepository.count({ - where: { parent: { uuid: space.uuid } }, - }); - - const children = await this.buildHierarchy(space, false, page, pageSize); - - return { - uuid: space.uuid, - name: space.spaceName, - type: space.spaceType.type, - totalCount, - children, - }; - } catch (err) { - if (err instanceof BadRequestException) { - throw err; // Re-throw BadRequestException - } else { - throw new HttpException('Unit not found', HttpStatus.NOT_FOUND); - } - } - } - - private async buildHierarchy( - space: SpaceEntity, - includeSubSpaces: boolean, - page: number, - pageSize: number, - ): Promise { - const children = await this.spaceRepository.find({ - where: { parent: { uuid: space.uuid } }, - relations: ['spaceType'], - skip: (page - 1) * pageSize, - take: pageSize, - }); - - if (!children || children.length === 0 || !includeSubSpaces) { - return children - .filter( - (child) => - child.spaceType.type !== SpaceType.UNIT && - child.spaceType.type !== SpaceType.FLOOR && - child.spaceType.type !== SpaceType.COMMUNITY && - child.spaceType.type !== SpaceType.UNIT, - ) // Filter remaining unit and floor and community and unit types - .map((child) => ({ - uuid: child.uuid, - name: child.spaceName, - type: child.spaceType.type, - })); - } - - const childHierarchies = await Promise.all( - children - .filter( - (child) => - child.spaceType.type !== SpaceType.UNIT && - child.spaceType.type !== SpaceType.FLOOR && - child.spaceType.type !== SpaceType.COMMUNITY && - child.spaceType.type !== SpaceType.UNIT, - ) // Filter remaining unit and floor and community and unit types - .map(async (child) => ({ - uuid: child.uuid, - name: child.spaceName, - type: child.spaceType.type, - children: await this.buildHierarchy(child, true, 1, pageSize), - })), - ); - - return childHierarchies; - } - - async getUnitParentByUuid(unitUuid: string): Promise { - try { - const unit = await this.spaceRepository.findOne({ - where: { - uuid: unitUuid, - spaceType: { - type: SpaceType.UNIT, - }, - }, - relations: ['spaceType', 'parent', 'parent.spaceType'], - }); - if (!unit || !unit.spaceType || unit.spaceType.type !== SpaceType.UNIT) { - throw new BadRequestException('Invalid unit UUID'); - } - return { - uuid: unit.uuid, - name: unit.spaceName, - type: unit.spaceType.type, - parent: { - uuid: unit.parent.uuid, - name: unit.parent.spaceName, - type: unit.parent.spaceType.type, - }, - }; - } catch (err) { - if (err instanceof BadRequestException) { - throw err; // Re-throw BadRequestException - } else { - throw new HttpException('Unit not found', HttpStatus.NOT_FOUND); - } - } - } - async getUnitsByUserId( - userUuid: string, - ): Promise { - try { - const units = await this.userSpaceRepository.find({ - relations: ['space', 'space.spaceType'], - where: { - user: { uuid: userUuid }, - space: { spaceType: { type: SpaceType.UNIT } }, - }, - }); - - if (units.length === 0) { - throw new HttpException('this user has no units', HttpStatus.NOT_FOUND); - } - const spaces = units.map((unit) => ({ - uuid: unit.space.uuid, - name: unit.space.spaceName, - type: unit.space.spaceType.type, - })); - - return spaces; - } catch (err) { - if (err instanceof HttpException) { - throw err; - } else { - throw new HttpException('user not found', HttpStatus.NOT_FOUND); - } - } - } - - async addUserUnit(addUserUnitDto: AddUserUnitDto) { - try { - return await this.userSpaceRepository.save({ - user: { uuid: addUserUnitDto.userUuid }, - space: { uuid: addUserUnitDto.unitUuid }, - }); - } 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 renameUnitByUuid( - unitUuid: string, - updateUnitNameDto: UpdateUnitNameDto, - ): Promise { - try { - const unit = await this.spaceRepository.findOneOrFail({ - where: { uuid: unitUuid }, - relations: ['spaceType'], - }); - - if (!unit || !unit.spaceType || unit.spaceType.type !== SpaceType.UNIT) { - throw new BadRequestException('Invalid unit UUID'); - } - - await this.spaceRepository.update( - { uuid: unitUuid }, - { spaceName: updateUnitNameDto.unitName }, - ); - - // Fetch the updated unit - const updatedUnit = await this.spaceRepository.findOneOrFail({ - where: { uuid: unitUuid }, - relations: ['spaceType'], - }); - - return { - uuid: updatedUnit.uuid, - name: updatedUnit.spaceName, - type: updatedUnit.spaceType.type, - }; - } catch (err) { - if (err instanceof BadRequestException) { - throw err; // Re-throw BadRequestException - } else { - throw new HttpException('Unit not found', HttpStatus.NOT_FOUND); - } - } - } - async getUnitInvitationCode(unitUuid: string): Promise { - try { - // Generate a 6-character random invitation code - const invitationCode = generateRandomString(6); - - // Update the unit with the new invitation code - await this.spaceRepository.update({ uuid: unitUuid }, { invitationCode }); - - // Fetch the updated unit - const updatedUnit = await this.spaceRepository.findOneOrFail({ - where: { uuid: unitUuid }, - relations: ['spaceType'], - }); - - return { - uuid: updatedUnit.uuid, - invitationCode: updatedUnit.invitationCode, - type: updatedUnit.spaceType.type, - }; - } catch (err) { - if (err instanceof BadRequestException) { - throw err; - } else { - throw new HttpException('Unit not found', HttpStatus.NOT_FOUND); - } - } - } - async verifyCodeAndAddUserUnit( - addUserUnitUsingCodeDto: AddUserUnitUsingCodeDto, - ) { - try { - const unit = await this.findUnitByInviteCode( - addUserUnitUsingCodeDto.inviteCode, - ); - - await this.addUserToUnit(addUserUnitUsingCodeDto.userUuid, unit.uuid); - - await this.clearUnitInvitationCode(unit.uuid); - - const deviceUUIDs = await this.getDeviceUUIDsForUnit(unit.uuid); - - await this.addUserPermissionsToDevices( - addUserUnitUsingCodeDto.userUuid, - deviceUUIDs, - ); - } catch (err) { - throw new HttpException( - 'Invalid invitation code', - HttpStatus.BAD_REQUEST, - ); - } - } - - private async findUnitByInviteCode(inviteCode: string): Promise { - const unit = await this.spaceRepository.findOneOrFail({ - where: { - invitationCode: inviteCode, - spaceType: { type: SpaceType.UNIT }, - }, - relations: ['spaceType'], - }); - - return unit; - } - - private async addUserToUnit(userUuid: string, unitUuid: string) { - const user = await this.addUserUnit({ userUuid, unitUuid }); - - if (user.uuid) { - return user; - } else { - throw new HttpException( - 'Failed to add user to unit', - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } - - private async clearUnitInvitationCode(unitUuid: string) { - await this.spaceRepository.update( - { uuid: unitUuid }, - { invitationCode: null }, - ); - } - - private async getDeviceUUIDsForUnit( - unitUuid: string, - ): Promise<{ uuid: string }[]> { - const devices = await this.spaceRepository.find({ - where: { parent: { uuid: unitUuid } }, - relations: ['devicesSpaceEntity', 'devicesSpaceEntity.productDevice'], - }); - - const allDevices = devices.flatMap((space) => space.devicesSpaceEntity); - - return allDevices.map((device) => ({ uuid: device.uuid })); - } - - private async addUserPermissionsToDevices( - userUuid: string, - deviceUUIDs: { uuid: string }[], - ): Promise { - 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); - } -} diff --git a/src/unit/unit.module.ts b/src/unit/unit.module.ts deleted file mode 100644 index 7ecd965..0000000 --- a/src/unit/unit.module.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Module } from '@nestjs/common'; -import { UnitService } from './services/unit.service'; -import { UnitController } from './controllers/unit.controller'; -import { ConfigModule } from '@nestjs/config'; -import { SpaceRepositoryModule } from '@app/common/modules/space/space.repository.module'; -import { SpaceRepository } from '@app/common/modules/space/repositories'; -import { SpaceTypeRepository } from '@app/common/modules/space/repositories'; -import { UserSpaceRepository } from '@app/common/modules/user/repositories'; -import { UserRepositoryModule } from '@app/common/modules/user/user.repository.module'; -import { UserRepository } from '@app/common/modules/user/repositories'; -import { UserDevicePermissionService } from 'src/user-device-permission/services'; -import { DeviceUserPermissionRepository } from '@app/common/modules/device/repositories'; -import { PermissionTypeRepository } from '@app/common/modules/permission/repositories'; - -@Module({ - imports: [ConfigModule, SpaceRepositoryModule, UserRepositoryModule], - controllers: [UnitController], - providers: [ - UnitService, - SpaceRepository, - SpaceTypeRepository, - UserSpaceRepository, - UserRepository, - UserDevicePermissionService, - DeviceUserPermissionRepository, - PermissionTypeRepository, - ], - exports: [UnitService], -}) -export class UnitModule {}