diff --git a/.gitignore b/.gitignore index 9823e7c..46ae420 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,9 @@ /node_modules /build +#github +/.github + # Logs logs *.log diff --git a/libs/common/src/constants/controller-route.ts b/libs/common/src/constants/controller-route.ts index 5d06090..ee2e727 100644 --- a/libs/common/src/constants/controller-route.ts +++ b/libs/common/src/constants/controller-route.ts @@ -558,6 +558,15 @@ export class ControllerRoute { }; }; + static DEVICE_PROJECT = class { + public static readonly ROUTE = '/projects/:projectUuid/devices'; + static ACTIONS = class { + public static readonly GET_ALL_DEVICES_SUMMARY = 'Get all devices'; + public static readonly GET_ALL_DEVICES_DESCRIPTION = + 'This endpoint retrieves all devices in the system.'; + }; + }; + static DEVICE_PERMISSION = class { public static readonly ROUTE = 'device-permission'; @@ -698,6 +707,8 @@ export class ControllerRoute { }; static VISITOR_PASSWORD = class { public static readonly ROUTE = 'visitor-password'; + public static readonly PROJECT_ROUTE = + '/projects/:projectUuid/visitor-password'; static ACTIONS = class { public static readonly ADD_ONLINE_TEMP_PASSWORD_MULTIPLE_TIME_SUMMARY = diff --git a/libs/common/src/firebase/devices-status/services/devices-status.service.ts b/libs/common/src/firebase/devices-status/services/devices-status.service.ts index fbbc00c..9ef06a0 100644 --- a/libs/common/src/firebase/devices-status/services/devices-status.service.ts +++ b/libs/common/src/firebase/devices-status/services/devices-status.service.ts @@ -10,7 +10,13 @@ import { GetDeviceDetailsFunctionsStatusInterface } from 'src/device/interfaces/ import { TuyaContext } from '@tuya/tuya-connector-nodejs'; import { ConfigService } from '@nestjs/config'; import { firebaseDataBase } from '../../firebase.config'; -import { Database, DataSnapshot, get, ref, set } from 'firebase/database'; +import { + Database, + DataSnapshot, + get, + ref, + runTransaction, +} from 'firebase/database'; import { DeviceStatusLogRepository } from '@app/common/modules/device-status-log/repositories'; @Injectable() export class DeviceStatusFirebaseService { @@ -154,39 +160,48 @@ export class DeviceStatusFirebaseService { this.firebaseDb, `device-status/${addDeviceStatusDto.deviceUuid}`, ); - const snapshot: DataSnapshot = await get(dataRef); - const existingData = snapshot.val() || {}; - // Assign default values if fields are not present - if (!existingData.deviceTuyaUuid) { - existingData.deviceTuyaUuid = addDeviceStatusDto.deviceTuyaUuid; - } - if (!existingData.productUuid) { - existingData.productUuid = addDeviceStatusDto.productUuid; - } - if (!existingData.productType) { - existingData.productType = addDeviceStatusDto.productType; - } - if (!existingData.status) { - existingData.status = []; - } + // Use a transaction to handle concurrent updates + await runTransaction(dataRef, (existingData) => { + if (!existingData) { + existingData = {}; + } - // Create a map to track existing status codes - const statusMap = new Map( - existingData.status.map((item) => [item.code, item.value]), - ); + // Assign default values if fields are not present + if (!existingData.deviceTuyaUuid) { + existingData.deviceTuyaUuid = addDeviceStatusDto.deviceTuyaUuid; + } + if (!existingData.productUuid) { + existingData.productUuid = addDeviceStatusDto.productUuid; + } + if (!existingData.productType) { + existingData.productType = addDeviceStatusDto.productType; + } + if (!existingData.status) { + existingData.status = []; + } - // Update or add status codes + // Create a map to track existing status codes + const statusMap = new Map( + existingData.status.map((item) => [item.code, item.value]), + ); - for (const statusItem of addDeviceStatusDto.status) { - statusMap.set(statusItem.code, statusItem.value); - } + // Update or add status codes - // Convert the map back to an array format - existingData.status = Array.from(statusMap, ([code, value]) => ({ - code, - value, - })); + for (const statusItem of addDeviceStatusDto.status) { + statusMap.set(statusItem.code, statusItem.value); + } + + // Convert the map back to an array format + existingData.status = Array.from(statusMap, ([code, value]) => ({ + code, + value, + })); + + return existingData; + }); + + // Save logs to your repository const newLogs = addDeviceStatusDto.log.properties.map((property) => { return this.deviceStatusLogRepository.create({ deviceId: addDeviceStatusDto.deviceUuid, @@ -200,10 +215,9 @@ export class DeviceStatusFirebaseService { }); }); await this.deviceStatusLogRepository.save(newLogs); - // Save the updated data to Firebase - await set(dataRef, existingData); // Return the updated data - return existingData; + const snapshot: DataSnapshot = await get(dataRef); + return snapshot.val(); } } diff --git a/src/automation/automation.module.ts b/src/automation/automation.module.ts index 810c8ec..1230238 100644 --- a/src/automation/automation.module.ts +++ b/src/automation/automation.module.ts @@ -16,6 +16,7 @@ import { } from '@app/common/modules/scene/repositories'; import { SceneDeviceRepository } from '@app/common/modules/scene-device/repositories'; import { AutomationRepository } from '@app/common/modules/automation/repositories'; +import { ProjectRepository } from '@app/common/modules/project/repositiories/project.repository'; @Module({ imports: [ConfigModule, SpaceRepositoryModule, DeviceStatusFirebaseModule], @@ -32,6 +33,7 @@ import { AutomationRepository } from '@app/common/modules/automation/repositorie SceneRepository, SceneDeviceRepository, AutomationRepository, + ProjectRepository, ], exports: [AutomationService], }) diff --git a/src/device/controllers/device-project.controller.ts b/src/device/controllers/device-project.controller.ts new file mode 100644 index 0000000..efdb6ca --- /dev/null +++ b/src/device/controllers/device-project.controller.ts @@ -0,0 +1,29 @@ +import { DeviceService } from '../services/device.service'; +import { Controller, Get, Param, UseGuards } from '@nestjs/common'; +import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger'; +import { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; +import { ControllerRoute } from '@app/common/constants/controller-route'; +import { PermissionsGuard } from 'src/guards/permissions.guard'; +import { Permissions } from 'src/decorators/permissions.decorator'; +import { ProjectParam } from '../dtos'; + +@ApiTags('Device Module') +@Controller({ + version: EnableDisableStatusEnum.ENABLED, + path: ControllerRoute.DEVICE_PROJECT.ROUTE, +}) +export class DeviceProjectController { + constructor(private readonly deviceService: DeviceService) {} + + @ApiBearerAuth() + @UseGuards(PermissionsGuard) + @Permissions('DEVICE_VIEW') + @Get() + @ApiOperation({ + summary: ControllerRoute.DEVICE.ACTIONS.GET_ALL_DEVICES_SUMMARY, + description: ControllerRoute.DEVICE.ACTIONS.GET_ALL_DEVICES_DESCRIPTION, + }) + async getAllDevices(@Param() param: ProjectParam) { + return await this.deviceService.getAllDevices(param); + } +} diff --git a/src/device/controllers/device.controller.ts b/src/device/controllers/device.controller.ts index 1ec8c6c..7bd9efd 100644 --- a/src/device/controllers/device.controller.ts +++ b/src/device/controllers/device.controller.ts @@ -224,17 +224,6 @@ export class DeviceController { async getDevicesInGateway(@Param('gatewayUuid') gatewayUuid: string) { return await this.deviceService.getDevicesInGateway(gatewayUuid); } - @ApiBearerAuth() - @UseGuards(PermissionsGuard) - @Permissions('DEVICE_VIEW') - @Get() - @ApiOperation({ - summary: ControllerRoute.DEVICE.ACTIONS.GET_ALL_DEVICES_SUMMARY, - description: ControllerRoute.DEVICE.ACTIONS.GET_ALL_DEVICES_DESCRIPTION, - }) - async getAllDevices() { - return await this.deviceService.getAllDevices(); - } @ApiBearerAuth() @UseGuards(PermissionsGuard) diff --git a/src/device/device.module.ts b/src/device/device.module.ts index 2a372cc..fdd3350 100644 --- a/src/device/device.module.ts +++ b/src/device/device.module.ts @@ -20,6 +20,8 @@ import { } from '@app/common/modules/scene/repositories'; import { SceneDeviceRepository } from '@app/common/modules/scene-device/repositories'; import { AutomationRepository } from '@app/common/modules/automation/repositories'; +import { ProjectRepository } from '@app/common/modules/project/repositiories'; +import { DeviceProjectController } from './controllers/device-project.controller'; @Module({ imports: [ ConfigModule, @@ -28,13 +30,14 @@ import { AutomationRepository } from '@app/common/modules/automation/repositorie DeviceRepositoryModule, DeviceStatusFirebaseModule, ], - controllers: [DeviceController], + controllers: [DeviceController, DeviceProjectController], providers: [ DeviceService, ProductRepository, DeviceUserPermissionRepository, PermissionTypeRepository, SpaceRepository, + ProjectRepository, DeviceRepository, UserRepository, TuyaService, diff --git a/src/device/dtos/index.ts b/src/device/dtos/index.ts index 1a0b6b3..2c6588d 100644 --- a/src/device/dtos/index.ts +++ b/src/device/dtos/index.ts @@ -1,3 +1,4 @@ export * from './add.device.dto'; export * from './control.device.dto'; export * from './get.device.dto'; +export * from './project.param.dto'; diff --git a/src/device/dtos/project.param.dto.ts b/src/device/dtos/project.param.dto.ts new file mode 100644 index 0000000..8bb2929 --- /dev/null +++ b/src/device/dtos/project.param.dto.ts @@ -0,0 +1,11 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsUUID } from 'class-validator'; + +export class ProjectParam { + @ApiProperty({ + description: 'UUID of the project this community belongs to', + example: 'd290f1ee-6c54-4b01-90e6-d701748f0851', + }) + @IsUUID() + projectUuid: string; +} diff --git a/src/device/services/device.service.ts b/src/device/services/device.service.ts index 949e105..c6d6f12 100644 --- a/src/device/services/device.service.ts +++ b/src/device/services/device.service.ts @@ -60,6 +60,8 @@ import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; import { DeleteSceneFromSceneDeviceDto } from '../dtos/delete.device.dto'; import { DeviceEntity } from '@app/common/modules/device/entities'; import { SpaceEntity } from '@app/common/modules/space/entities/space.entity'; +import { ProjectRepository } from '@app/common/modules/project/repositiories'; +import { ProjectParam } from '../dtos'; @Injectable() export class DeviceService { @@ -74,6 +76,7 @@ export class DeviceService { @Inject(forwardRef(() => SceneService)) private readonly sceneService: SceneService, private readonly tuyaService: TuyaService, + private readonly projectRepository: ProjectRepository, ) { const accessKey = this.configService.get('auth-config.ACCESS_KEY'); const secretKey = this.configService.get('auth-config.SECRET_KEY'); @@ -950,10 +953,19 @@ export class DeviceService { ); } } - async getAllDevices(): Promise { + async getAllDevices( + param: ProjectParam, + ): Promise { try { + await this.validateProject(param.projectUuid); + const devices = await this.deviceRepository.find({ - where: { isActive: true }, + where: { + isActive: true, + spaceDevice: { + community: { project: { uuid: param.projectUuid } }, + }, + }, relations: [ 'spaceDevice.parent', 'spaceDevice.community', @@ -1009,10 +1021,13 @@ export class DeviceService { } } - const spaceHierarchy = await this.getFullSpaceHierarchy( + const spaceHierarchy = await this.getParentHierarchy( device?.spaceDevice, ); - const orderedHierarchy = spaceHierarchy.reverse(); + const orderedHierarchy = [ + device?.spaceDevice, + ...spaceHierarchy.reverse(), + ]; return { spaces: orderedHierarchy.map((space) => ({ @@ -1490,4 +1505,45 @@ export class DeviceService { ); } } + + private async validateProject(uuid: string) { + const project = await this.projectRepository.findOne({ + where: { uuid }, + }); + if (!project) { + throw new HttpException( + `A project with the uuid '${uuid}' doesn't exists.`, + HttpStatus.BAD_REQUEST, + ); + } + return project; + } + + async getParentHierarchy( + space: SpaceEntity, + ): Promise<{ uuid: string; spaceName: string }[]> { + try { + const targetSpace = await this.spaceRepository.findOne({ + where: { uuid: space.uuid }, + relations: ['parent'], + }); + + if (!targetSpace) { + throw new HttpException('Space not found', HttpStatus.NOT_FOUND); + } + + const ancestors = await this.fetchAncestors(targetSpace); + + return ancestors.map((parentSpace) => ({ + uuid: parentSpace.uuid, + spaceName: parentSpace.spaceName, + })); + } catch (error) { + console.error('Error fetching parent hierarchy:', error.message); + throw new HttpException( + 'Error fetching parent hierarchy', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } } diff --git a/src/door-lock/door.lock.module.ts b/src/door-lock/door.lock.module.ts index 3cf563b..2e00893 100644 --- a/src/door-lock/door.lock.module.ts +++ b/src/door-lock/door.lock.module.ts @@ -19,6 +19,7 @@ import { } from '@app/common/modules/scene/repositories'; import { SceneDeviceRepository } from '@app/common/modules/scene-device/repositories'; import { AutomationRepository } from '@app/common/modules/automation/repositories'; +import { ProjectRepository } from '@app/common/modules/project/repositiories'; @Module({ imports: [ConfigModule, DeviceRepositoryModule], controllers: [DoorLockController], @@ -32,6 +33,7 @@ import { AutomationRepository } from '@app/common/modules/automation/repositorie DeviceStatusFirebaseService, SpaceRepository, DeviceStatusLogRepository, + ProjectRepository, TuyaService, SceneService, SceneIconRepository, diff --git a/src/scene/scene.module.ts b/src/scene/scene.module.ts index a6a1435..0fe961b 100644 --- a/src/scene/scene.module.ts +++ b/src/scene/scene.module.ts @@ -15,6 +15,7 @@ import { import { TuyaService } from '@app/common/integrations/tuya/services/tuya.service'; import { SceneDeviceRepository } from '@app/common/modules/scene-device/repositories'; import { AutomationRepository } from '@app/common/modules/automation/repositories'; +import { ProjectRepository } from '@app/common/modules/project/repositiories'; @Module({ imports: [ConfigModule, SpaceRepositoryModule, DeviceStatusFirebaseModule], @@ -28,6 +29,7 @@ import { AutomationRepository } from '@app/common/modules/automation/repositorie ProductRepository, SceneIconRepository, SceneRepository, + ProjectRepository, SceneDeviceRepository, AutomationRepository, ], diff --git a/src/space-model/commands/propagate-space-model-deletion.command.ts b/src/space-model/commands/propagate-space-model-deletion.command.ts index 0cacd5b..f180e71 100644 --- a/src/space-model/commands/propagate-space-model-deletion.command.ts +++ b/src/space-model/commands/propagate-space-model-deletion.command.ts @@ -1,9 +1,11 @@ import { SpaceModelEntity } from '@app/common/modules/space-model'; +import { QueryRunner } from 'typeorm'; export class PropogateDeleteSpaceModelCommand { constructor( public readonly param: { spaceModel: SpaceModelEntity; + queryRunner: QueryRunner; }, ) {} } diff --git a/src/space-model/handlers/propogate-space-model-deletion.handler.ts b/src/space-model/handlers/propogate-space-model-deletion.handler.ts index acd690e..0e29222 100644 --- a/src/space-model/handlers/propogate-space-model-deletion.handler.ts +++ b/src/space-model/handlers/propogate-space-model-deletion.handler.ts @@ -19,8 +19,7 @@ export class PropogateDeleteSpaceModelHandler ) {} async execute(command: PropogateDeleteSpaceModelCommand): Promise { - const { spaceModel } = command.param; - const queryRunner = this.dataSource.createQueryRunner(); + const { spaceModel, queryRunner } = command.param; try { await queryRunner.connect(); diff --git a/src/space-model/services/space-model.service.ts b/src/space-model/services/space-model.service.ts index 9a49df0..de9df6b 100644 --- a/src/space-model/services/space-model.service.ts +++ b/src/space-model/services/space-model.service.ts @@ -25,6 +25,7 @@ import { BaseResponseDto } from '@app/common/dto/base.response.dto'; import { CommandBus } from '@nestjs/cqrs'; import { ProcessTagDto } from 'src/tags/dtos'; import { SpaceModelProductAllocationService } from './space-model-product-allocation.service'; +import { PropogateDeleteSpaceModelCommand } from '../commands'; import { SpaceProductAllocationRepository, SpaceRepository, @@ -283,6 +284,13 @@ export class SpaceModelService { { disabled: true }, ); + await this.commandBus.execute( + new PropogateDeleteSpaceModelCommand({ + spaceModel: spaceModel, + queryRunner, + }), + ); + await queryRunner.commitTransaction(); return new SuccessResponseDto({ diff --git a/src/space/services/space-device.service.ts b/src/space/services/space-device.service.ts index 490cca7..773c7d5 100644 --- a/src/space/services/space-device.service.ts +++ b/src/space/services/space-device.service.ts @@ -32,10 +32,10 @@ export class SpaceDeviceService { ); } - // Fetch space hierarchy **once** and reverse it to get an ordered hierarchy const spaceHierarchy = - await this.validationService.getFullSpaceHierarchy(space); - const orderedHierarchy = spaceHierarchy.reverse(); + await this.validationService.getParentHierarchy(space); + + const orderedHierarchy = [space, ...spaceHierarchy.reverse()]; // Fetch Tuya details for each device in parallel using Promise.allSettled const deviceDetailsPromises = space.devices.map((device) => diff --git a/src/space/services/space-validation.service.ts b/src/space/services/space-validation.service.ts index 3bfd455..4184a5f 100644 --- a/src/space/services/space-validation.service.ts +++ b/src/space/services/space-validation.service.ts @@ -222,4 +222,32 @@ export class ValidationService { return descendants; } + + async getParentHierarchy( + space: SpaceEntity, + ): Promise<{ uuid: string; spaceName: string }[]> { + try { + const targetSpace = await this.spaceRepository.findOne({ + where: { uuid: space.uuid }, + relations: ['parent'], + }); + + if (!targetSpace) { + throw new HttpException('Space not found', HttpStatus.NOT_FOUND); + } + + const ancestors = await this.fetchAncestors(targetSpace); + + return ancestors.map((parentSpace) => ({ + uuid: parentSpace.uuid, + spaceName: parentSpace.spaceName, + })); + } catch (error) { + console.error('Error fetching parent hierarchy:', error.message); + throw new HttpException( + 'Error fetching parent hierarchy', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } } diff --git a/src/space/services/subspace/subspace-device.service.ts b/src/space/services/subspace/subspace-device.service.ts index cdd80db..8fc062c 100644 --- a/src/space/services/subspace/subspace-device.service.ts +++ b/src/space/services/subspace/subspace-device.service.ts @@ -84,7 +84,7 @@ export class SubspaceDeviceService { if (device.tag?.subspace?.uuid !== subspace.uuid) { await this.tagRepository.update( { uuid: device.tag.uuid }, - { subspace }, + { subspace, space: null }, ); } diff --git a/src/users/services/user.service.ts b/src/users/services/user.service.ts index 268dc7c..bda1c3a 100644 --- a/src/users/services/user.service.ts +++ b/src/users/services/user.service.ts @@ -30,7 +30,7 @@ export class UserService { where: { uuid: userUuid, }, - relations: ['region', 'timezone', 'roleType'], + relations: ['region', 'timezone', 'roleType', 'project'], }); if (!user) { throw new BadRequestException('Invalid room UUID'); @@ -52,6 +52,7 @@ export class UserService { hasAcceptedAppAgreement: user?.hasAcceptedAppAgreement, appAgreementAcceptedAt: user?.appAgreementAcceptedAt, role: user?.roleType, + project: user?.project, }; } catch (err) { if (err instanceof BadRequestException) { diff --git a/src/vistor-password/controllers/index.ts b/src/vistor-password/controllers/index.ts index ca4b986..b579b2d 100644 --- a/src/vistor-password/controllers/index.ts +++ b/src/vistor-password/controllers/index.ts @@ -1 +1,2 @@ export * from './visitor-password.controller'; +export * from './project-visitor-password.controller'; \ No newline at end of file diff --git a/src/vistor-password/controllers/project-visitor-password.controller.ts b/src/vistor-password/controllers/project-visitor-password.controller.ts new file mode 100644 index 0000000..f72b5c9 --- /dev/null +++ b/src/vistor-password/controllers/project-visitor-password.controller.ts @@ -0,0 +1,48 @@ +import { VisitorPasswordService } from '../services/visitor-password.service'; +import { Controller, UseGuards, Get, Param } from '@nestjs/common'; +import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger'; +import { Permissions } from 'src/decorators/permissions.decorator'; + +import { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; +import { ControllerRoute } from '@app/common/constants/controller-route'; +import { ProjectParam } from 'src/community/dtos'; +import { PermissionsGuard } from 'src/guards/permissions.guard'; + +@ApiTags('Visitor Password Module') +@Controller({ + version: EnableDisableStatusEnum.ENABLED, + path: ControllerRoute.VISITOR_PASSWORD.PROJECT_ROUTE, +}) +export class VisitorPasswordProjectController { + constructor( + private readonly visitorPasswordService: VisitorPasswordService, + ) {} + + @ApiBearerAuth() + @UseGuards(PermissionsGuard) + @Permissions('VISITOR_PASSWORD_ADD') + @Get() + @ApiOperation({ + summary: + ControllerRoute.VISITOR_PASSWORD.ACTIONS.GET_VISITOR_PASSWORD_SUMMARY, + description: + ControllerRoute.VISITOR_PASSWORD.ACTIONS.GET_VISITOR_PASSWORD_DESCRIPTION, + }) + async GetVisitorPassword(@Param() param: ProjectParam) { + return await this.visitorPasswordService.getPasswords(param); + } + + @ApiBearerAuth() + @UseGuards(PermissionsGuard) + @Permissions('VISITOR_PASSWORD_VIEW') + @Get('/devices') + @ApiOperation({ + summary: + ControllerRoute.VISITOR_PASSWORD.ACTIONS.GET_VISITOR_DEVICES_SUMMARY, + description: + ControllerRoute.VISITOR_PASSWORD.ACTIONS.GET_VISITOR_DEVICES_DESCRIPTION, + }) + async GetVisitorDevices(@Param() param: ProjectParam) { + return await this.visitorPasswordService.getAllPassDevices(param); + } +} diff --git a/src/vistor-password/controllers/visitor-password.controller.ts b/src/vistor-password/controllers/visitor-password.controller.ts index a298114..3e1b781 100644 --- a/src/vistor-password/controllers/visitor-password.controller.ts +++ b/src/vistor-password/controllers/visitor-password.controller.ts @@ -5,7 +5,6 @@ import { Post, HttpStatus, UseGuards, - Get, Req, } from '@nestjs/common'; import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger'; @@ -146,32 +145,4 @@ export class VisitorPasswordController { data: temporaryPassword, }; } - - @ApiBearerAuth() - @UseGuards(PermissionsGuard) - @Permissions('VISITOR_PASSWORD_VIEW') - @Get() - @ApiOperation({ - summary: - ControllerRoute.VISITOR_PASSWORD.ACTIONS.GET_VISITOR_PASSWORD_SUMMARY, - description: - ControllerRoute.VISITOR_PASSWORD.ACTIONS.GET_VISITOR_PASSWORD_DESCRIPTION, - }) - async GetVisitorPassword() { - return await this.visitorPasswordService.getPasswords(); - } - - @ApiBearerAuth() - @UseGuards(PermissionsGuard) - @Permissions('VISITOR_PASSWORD_VIEW') - @Get('/devices') - @ApiOperation({ - summary: - ControllerRoute.VISITOR_PASSWORD.ACTIONS.GET_VISITOR_DEVICES_SUMMARY, - description: - ControllerRoute.VISITOR_PASSWORD.ACTIONS.GET_VISITOR_DEVICES_DESCRIPTION, - }) - async GetVisitorDevices() { - return await this.visitorPasswordService.getAllPassDevices(); - } } diff --git a/src/vistor-password/dtos/index.ts b/src/vistor-password/dtos/index.ts index aa0f905..3bd7511 100644 --- a/src/vistor-password/dtos/index.ts +++ b/src/vistor-password/dtos/index.ts @@ -1 +1,2 @@ export * from './temp-pass.dto'; +export * from './project.param.dto'; \ No newline at end of file diff --git a/src/vistor-password/dtos/project.param.dto.ts b/src/vistor-password/dtos/project.param.dto.ts new file mode 100644 index 0000000..8bb2929 --- /dev/null +++ b/src/vistor-password/dtos/project.param.dto.ts @@ -0,0 +1,11 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsUUID } from 'class-validator'; + +export class ProjectParam { + @ApiProperty({ + description: 'UUID of the project this community belongs to', + example: 'd290f1ee-6c54-4b01-90e6-d701748f0851', + }) + @IsUUID() + projectUuid: string; +} diff --git a/src/vistor-password/services/visitor-password.service.ts b/src/vistor-password/services/visitor-password.service.ts index b8710fa..2f08f0c 100644 --- a/src/vistor-password/services/visitor-password.service.ts +++ b/src/vistor-password/services/visitor-password.service.ts @@ -14,6 +14,7 @@ import { AddDoorLockOfflineOneTimeDto, AddDoorLockOnlineMultipleDto, AddDoorLockOnlineOneTimeDto, + ProjectParam, } from '../dtos'; import { EmailService } from '@app/common/util/email.service'; import { PasswordEncryptionService } from 'src/door-lock/services/encryption.services'; @@ -30,6 +31,7 @@ import { CommonHourMinutes, CommonHours, } from '@app/common/constants/hours-minutes.enum'; +import { ProjectRepository } from '@app/common/modules/project/repositiories'; @Injectable() export class VisitorPasswordService { @@ -42,6 +44,7 @@ export class VisitorPasswordService { private readonly doorLockService: DoorLockService, private readonly deviceService: DeviceService, private readonly passwordEncryptionService: PasswordEncryptionService, + private readonly projectRepository: ProjectRepository, ) { const accessKey = this.configService.get('auth-config.ACCESS_KEY'); const secretKey = this.configService.get('auth-config.SECRET_KEY'); @@ -444,12 +447,21 @@ export class VisitorPasswordService { ); } } - async getPasswords() { + async getPasswords(param: ProjectParam) { + await this.validateProject(param.projectUuid); + const deviceIds = await this.deviceRepository.find({ where: { productDevice: { prodType: ProductType.DL, }, + spaceDevice: { + community: { + project: { + uuid: param.projectUuid, + }, + }, + }, isActive: true, }, }); @@ -487,15 +499,25 @@ export class VisitorPasswordService { }); } - async getAllPassDevices() { + async getAllPassDevices(param: ProjectParam) { + await this.validateProject(param.projectUuid); + const devices = await this.deviceRepository.find({ where: { productDevice: { prodType: ProductType.DL, }, + spaceDevice: { + community: { + project: { + uuid: param.projectUuid, + }, + }, + }, isActive: true, }, - relations: ['productDevice'], + + relations: ['productDevice', 'spaceDevice'], }); const devicesData = await Promise.all( devices?.map(async (device) => { @@ -509,6 +531,7 @@ export class VisitorPasswordService { productType: device.productDevice.prodType, ...deviceDetails, uuid: device.uuid, + spaceName: device.spaceDevice.spaceName, } as GetDeviceDetailsInterface; } catch (error) { console.error( @@ -907,4 +930,17 @@ export class VisitorPasswordService { ); } } + + private async validateProject(uuid: string) { + const project = await this.projectRepository.findOne({ + where: { uuid }, + }); + if (!project) { + throw new HttpException( + `A project with the uuid '${uuid}' doesn't exists.`, + HttpStatus.BAD_REQUEST, + ); + } + return project; + } } diff --git a/src/vistor-password/visitor-password.module.ts b/src/vistor-password/visitor-password.module.ts index 3aaf3a5..5ee1f0e 100644 --- a/src/vistor-password/visitor-password.module.ts +++ b/src/vistor-password/visitor-password.module.ts @@ -21,9 +21,11 @@ import { } from '@app/common/modules/scene/repositories'; import { SceneDeviceRepository } from '@app/common/modules/scene-device/repositories'; import { AutomationRepository } from '@app/common/modules/automation/repositories'; +import { ProjectRepository } from '@app/common/modules/project/repositiories'; +import { VisitorPasswordProjectController } from './controllers'; @Module({ imports: [ConfigModule, DeviceRepositoryModule, DoorLockModule], - controllers: [VisitorPasswordController], + controllers: [VisitorPasswordController, VisitorPasswordProjectController], providers: [ VisitorPasswordService, EmailService, @@ -41,6 +43,7 @@ import { AutomationRepository } from '@app/common/modules/automation/repositorie SceneRepository, SceneDeviceRepository, AutomationRepository, + ProjectRepository, ], exports: [VisitorPasswordService], })