removed unit,floor,building,room modules

This commit is contained in:
hannathkadher
2024-10-22 10:02:54 +04:00
parent 581618296d
commit 3bd18c2b29
53 changed files with 110 additions and 2416 deletions

View File

@ -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,

View File

@ -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<GetUnitByUuidInterface> {
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) {

View File

@ -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 {}

View File

@ -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;
}
}

View File

@ -1 +0,0 @@
export * from './building.controller';

View File

@ -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<AddBuildingDto>) {
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<AddUserBuildingDto>) {
Object.assign(this, dto);
}
}

View File

@ -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;
}

View File

@ -1 +0,0 @@
export * from './add.building.dto';

View File

@ -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<UpdateBuildingNameDto>) {
Object.assign(this, dto);
}
}

View File

@ -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;
}

View File

@ -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<GetBuildingByUuidInterface> {
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<BuildingChildInterface> {
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<BuildingChildInterface[]> {
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<BuildingParentInterface> {
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<GetBuildingByUserUuidInterface[]> {
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<RenameBuildingByUuidInterface> {
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);
}
}
}
}

View File

@ -1 +0,0 @@
export * from './building.service';

View File

@ -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<SpaceEntity[]> {
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<SpaceEntity[]> {
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;
}
}

View File

@ -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;
}
}

View File

@ -1 +0,0 @@
export * from './floor.controller';

View File

@ -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<AddFloorDto>) {
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<AddUserFloorDto>) {
Object.assign(this, dto);
}
}

View File

@ -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;
}

View File

@ -1 +0,0 @@
export * from './add.floor.dto';

View File

@ -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<UpdateFloorNameDto>) {
Object.assign(this, dto);
}
}

View File

@ -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 {}

View File

@ -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;
}

View File

@ -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<GetFloorByUuidInterface> {
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<FloorChildInterface> {
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<FloorChildInterface[]> {
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<FloorParentInterface> {
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<GetFloorByUserUuidInterface[]> {
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<RenameFloorByUuidInterface> {
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);
}
}
}
}

View File

@ -1 +0,0 @@
export * from './floor.service';

View File

@ -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');
}
}

View File

@ -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');
}
}

View File

@ -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');
}
}

View File

@ -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) {

View File

@ -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');
}
}

View File

@ -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) {

View File

@ -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');

View File

@ -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');

View File

@ -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) {

View File

@ -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) {

View File

@ -1 +0,0 @@
export * from './room.controller';

View File

@ -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;
}
}

View File

@ -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<AddRoomDto>) {
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<AddUserRoomDto>) {
Object.assign(this, dto);
}
}

View File

@ -1 +0,0 @@
export * from './add.room.dto';

View File

@ -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<UpdateRoomNameDto>) {
Object.assign(this, dto);
}
}

View File

@ -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;
}

View File

@ -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 {}

View File

@ -1 +0,0 @@
export * from './room.service';

View File

@ -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<GetRoomByUuidInterface> {
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<RoomParentInterface> {
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<GetRoomByUserUuidInterface[]> {
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<RenameRoomByUuidInterface> {
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);
}
}
}
}

View File

@ -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<GetUnitByUuidInterface> {
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) {

View File

@ -1 +0,0 @@
export * from './unit.controller';

View File

@ -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',
};
}
}

View File

@ -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<AddUnitDto>) {
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<AddUserUnitDto>) {
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<AddUserUnitDto>) {
Object.assign(this, dto);
}
}

View File

@ -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;
}

View File

@ -1 +0,0 @@
export * from './add.unit.dto';

View File

@ -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<UpdateUnitNameDto>) {
Object.assign(this, dto);
}
}

View File

@ -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;
}

View File

@ -1 +0,0 @@
export * from './unit.service';

View File

@ -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<string>('auth-config.ACCESS_KEY');
const secretKey = this.configService.get<string>('auth-config.SECRET_KEY');
const tuyaEuUrl = this.configService.get<string>('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<addTuyaSpaceInterface> {
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<GetUnitByUuidInterface> {
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<UnitChildInterface> {
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<UnitChildInterface[]> {
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<UnitParentInterface> {
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<GetUnitByUserUuidInterface[]> {
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<RenameUnitByUuidInterface> {
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<any> {
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<SpaceEntity> {
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<void> {
const permissionPromises = deviceUUIDs.map(async (device) => {
try {
await this.userDevicePermissionService.addUserPermission({
userUuid,
deviceUuid: device.uuid,
permissionType: PermissionType.CONTROLLABLE,
});
} catch (error) {
console.error(
`Failed to add permission for device ${device.uuid}: ${error.message}`,
);
}
});
await Promise.all(permissionPromises);
}
}

View File

@ -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 {}