finished refactor vistitor password device

This commit is contained in:
faris Aljohari
2025-03-24 03:28:26 +03:00
parent 8efd8cfba2
commit 6785fb2b1e
6 changed files with 275 additions and 213 deletions

View File

@ -0,0 +1,3 @@
export enum DeviceTypeEnum {
DOOR_LOCK = 'DOOR_LOCK',
}

View File

@ -1,11 +1,11 @@
import { DeviceService } from '../services/device.service'; import { DeviceService } from '../services/device.service';
import { Controller, Get, Param, UseGuards } from '@nestjs/common'; import { Controller, Get, Param, Query, UseGuards } from '@nestjs/common';
import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger'; import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger';
import { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; import { EnableDisableStatusEnum } from '@app/common/constants/days.enum';
import { ControllerRoute } from '@app/common/constants/controller-route'; import { ControllerRoute } from '@app/common/constants/controller-route';
import { PermissionsGuard } from 'src/guards/permissions.guard'; import { PermissionsGuard } from 'src/guards/permissions.guard';
import { Permissions } from 'src/decorators/permissions.decorator'; import { Permissions } from 'src/decorators/permissions.decorator';
import { ProjectParam } from '../dtos'; import { GetDoorLockDevices, ProjectParam } from '../dtos';
@ApiTags('Device Module') @ApiTags('Device Module')
@Controller({ @Controller({
@ -23,7 +23,10 @@ export class DeviceProjectController {
summary: ControllerRoute.DEVICE.ACTIONS.GET_ALL_DEVICES_SUMMARY, summary: ControllerRoute.DEVICE.ACTIONS.GET_ALL_DEVICES_SUMMARY,
description: ControllerRoute.DEVICE.ACTIONS.GET_ALL_DEVICES_DESCRIPTION, description: ControllerRoute.DEVICE.ACTIONS.GET_ALL_DEVICES_DESCRIPTION,
}) })
async getAllDevices(@Param() param: ProjectParam) { async getAllDevices(
return await this.deviceService.getAllDevices(param); @Param() param: ProjectParam,
@Query() query: GetDoorLockDevices,
) {
return await this.deviceService.getAllDevices(param, query);
} }
} }

View File

@ -1,5 +1,6 @@
import { DeviceTypeEnum } from '@app/common/constants/device-type.enum';
import { ApiProperty } from '@nestjs/swagger'; import { ApiProperty } from '@nestjs/swagger';
import { IsNotEmpty, IsOptional, IsString } from 'class-validator'; import { IsEnum, IsNotEmpty, IsOptional, IsString } from 'class-validator';
export class GetDeviceBySpaceUuidDto { export class GetDeviceBySpaceUuidDto {
@ApiProperty({ @ApiProperty({
@ -33,3 +34,13 @@ export class GetDeviceLogsDto {
@IsOptional() @IsOptional()
public endTime: string; public endTime: string;
} }
export class GetDoorLockDevices {
@ApiProperty({
description: 'Device Type',
enum: DeviceTypeEnum,
required: false,
})
@IsEnum(DeviceTypeEnum)
@IsOptional()
public deviceType: DeviceTypeEnum;
}

View File

@ -31,6 +31,7 @@ import {
import { import {
GetDeviceBySpaceUuidDto, GetDeviceBySpaceUuidDto,
GetDeviceLogsDto, GetDeviceLogsDto,
GetDoorLockDevices,
} from '../dtos/get.device.dto'; } from '../dtos/get.device.dto';
import { import {
BatchControlDevicesDto, BatchControlDevicesDto,
@ -63,6 +64,7 @@ import { SpaceEntity } from '@app/common/modules/space/entities/space.entity';
import { ProjectRepository } from '@app/common/modules/project/repositiories'; import { ProjectRepository } from '@app/common/modules/project/repositiories';
import { ProjectParam } from '../dtos'; import { ProjectParam } from '../dtos';
import { BatchDeviceTypeEnum } from '@app/common/constants/batch-device.enum'; import { BatchDeviceTypeEnum } from '@app/common/constants/batch-device.enum';
import { DeviceTypeEnum } from '@app/common/constants/device-type.enum';
@Injectable() @Injectable()
export class DeviceService { export class DeviceService {
@ -980,10 +982,15 @@ export class DeviceService {
} }
} }
async getAllDevices(param: ProjectParam): Promise<BaseResponseDto> { async getAllDevices(
param: ProjectParam,
query: GetDoorLockDevices,
): Promise<BaseResponseDto> {
try { try {
await this.validateProject(param.projectUuid); await this.validateProject(param.projectUuid);
if (query.deviceType === DeviceTypeEnum.DOOR_LOCK) {
return await this.getDoorLockDevices(param.projectUuid);
} else if (!query.deviceType) {
const devices = await this.deviceRepository.find({ const devices = await this.deviceRepository.find({
where: { where: {
isActive: true, isActive: true,
@ -1097,7 +1104,8 @@ export class DeviceService {
.filter((result) => result.status === DeviceStatuses.FULLFILLED) .filter((result) => result.status === DeviceStatuses.FULLFILLED)
.map( .map(
(result) => (result) =>
(result as PromiseFulfilledResult<GetDeviceDetailsInterface>).value, (result as PromiseFulfilledResult<GetDeviceDetailsInterface>)
.value,
); );
return new SuccessResponseDto({ return new SuccessResponseDto({
@ -1105,6 +1113,7 @@ export class DeviceService {
data: fulfilledDevices, data: fulfilledDevices,
statusCode: HttpStatus.OK, statusCode: HttpStatus.OK,
}); });
}
} catch (error) { } catch (error) {
throw new HttpException( throw new HttpException(
error.message || 'Internal server error', error.message || 'Internal server error',
@ -1650,4 +1659,60 @@ export class DeviceService {
{ spaceDevice: targetSpace }, { spaceDevice: targetSpace },
); );
} }
async getDoorLockDevices(projectUuid: string) {
await this.validateProject(projectUuid);
const devices = await this.deviceRepository.find({
where: {
productDevice: {
prodType: ProductType.DL,
},
spaceDevice: {
spaceName: Not(ORPHAN_SPACE_NAME),
community: {
project: {
uuid: projectUuid,
},
},
},
isActive: true,
},
relations: ['productDevice', 'spaceDevice'],
});
const devicesData = await Promise.all(
devices?.map(async (device) => {
try {
const deviceDetails = await this.getDeviceDetailsByDeviceIdTuya(
device.deviceTuyaUuid,
);
return {
productUuid: device.productDevice.uuid,
productType: device.productDevice.prodType,
...deviceDetails,
uuid: device.uuid,
spaceName: device.spaceDevice.spaceName,
} as GetDeviceDetailsInterface;
} catch (error) {
console.error(
`Error fetching details for device ${device.deviceTuyaUuid}:`,
error,
);
// Return null to indicate the error
return null;
}
}),
);
// Filter out null values to only include successful device data
const filteredDevicesData = devicesData.filter(
(deviceData) => deviceData !== null,
);
return new SuccessResponseDto({
message: 'Successfully retrieved all pass devices',
data: filteredDevicesData,
statusCode: HttpStatus.OK,
});
}
} }

View File

@ -1,13 +1,5 @@
import { VisitorPasswordService } from '../services/visitor-password.service'; import { VisitorPasswordService } from '../services/visitor-password.service';
import { import { Body, Controller, Post, UseGuards, Req, Get } from '@nestjs/common';
Body,
Controller,
Post,
HttpStatus,
UseGuards,
Req,
Get,
} from '@nestjs/common';
import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger'; import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger';
import { AddDoorLockTemporaryPasswordDto } from '../dtos/temp-pass.dto'; import { AddDoorLockTemporaryPasswordDto } from '../dtos/temp-pass.dto';
import { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; import { EnableDisableStatusEnum } from '@app/common/constants/days.enum';
@ -40,16 +32,12 @@ export class VisitorPasswordController {
@Req() req: any, @Req() req: any,
) { ) {
const userUuid = req.user.uuid; const userUuid = req.user.uuid;
const temporaryPasswords = const projectUuid = req.user.project.uuid;
await this.visitorPasswordService.handleTemporaryPassword( return await this.visitorPasswordService.handleTemporaryPassword(
addDoorLockTemporaryPasswordDto, addDoorLockTemporaryPasswordDto,
userUuid, userUuid,
projectUuid,
); );
return {
statusCode: HttpStatus.CREATED,
data: temporaryPasswords,
};
} }
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(PermissionsGuard) @UseGuards(PermissionsGuard)
@ -65,19 +53,4 @@ export class VisitorPasswordController {
const projectUuid = req.user.project.uuid; const projectUuid = req.user.project.uuid;
return await this.visitorPasswordService.getPasswords(projectUuid); return await this.visitorPasswordService.getPasswords(projectUuid);
} }
@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(@Req() req: any) {
const projectUuid = req.user.project.uuid;
return await this.visitorPasswordService.getAllPassDevices(projectUuid);
}
} }

View File

@ -18,7 +18,6 @@ import { AddDoorLockTemporaryPasswordDto } from '../dtos';
import { EmailService } from '@app/common/util/email.service'; import { EmailService } from '@app/common/util/email.service';
import { PasswordEncryptionService } from 'src/door-lock/services/encryption.services'; import { PasswordEncryptionService } from 'src/door-lock/services/encryption.services';
import { DoorLockService } from 'src/door-lock/services'; import { DoorLockService } from 'src/door-lock/services';
import { GetDeviceDetailsInterface } from 'src/device/interfaces/get.device.interface';
import { DeviceService } from 'src/device/services'; import { DeviceService } from 'src/device/services';
import { DeviceStatuses } from '@app/common/constants/device-status.enum'; import { DeviceStatuses } from '@app/common/constants/device-status.enum';
import { import {
@ -32,6 +31,9 @@ import {
} from '@app/common/constants/hours-minutes.enum'; } from '@app/common/constants/hours-minutes.enum';
import { ProjectRepository } from '@app/common/modules/project/repositiories'; import { ProjectRepository } from '@app/common/modules/project/repositiories';
import { VisitorPasswordEnum } from '@app/common/constants/visitor-password.enum'; import { VisitorPasswordEnum } from '@app/common/constants/visitor-password.enum';
import { SuccessResponseDto } from '@app/common/dto/success.response.dto';
import { Not } from 'typeorm';
import { ORPHAN_SPACE_NAME } from '@app/common/constants/orphan-constant';
@Injectable() @Injectable()
export class VisitorPasswordService { export class VisitorPasswordService {
@ -58,43 +60,65 @@ export class VisitorPasswordService {
async handleTemporaryPassword( async handleTemporaryPassword(
addDoorLockTemporaryPasswordDto: AddDoorLockTemporaryPasswordDto, addDoorLockTemporaryPasswordDto: AddDoorLockTemporaryPasswordDto,
userUuid: string, userUuid: string,
projectUuid: string,
) { ) {
const { operationType } = addDoorLockTemporaryPasswordDto; const { operationType } = addDoorLockTemporaryPasswordDto;
let result;
switch (operationType) { switch (operationType) {
case VisitorPasswordEnum.ONLINE_ONE_TIME: case VisitorPasswordEnum.ONLINE_ONE_TIME:
return this.addOnlineTemporaryPasswordOneTime( result = await this.addOnlineTemporaryPasswordOneTime(
addDoorLockTemporaryPasswordDto, addDoorLockTemporaryPasswordDto,
userUuid, userUuid,
projectUuid,
); );
break;
case VisitorPasswordEnum.ONLINE_MULTIPLE_TIME: case VisitorPasswordEnum.ONLINE_MULTIPLE_TIME:
return this.addOnlineTemporaryPasswordMultipleTime( result = await this.addOnlineTemporaryPasswordMultipleTime(
addDoorLockTemporaryPasswordDto, addDoorLockTemporaryPasswordDto,
userUuid, userUuid,
projectUuid,
); );
break;
case VisitorPasswordEnum.OFFLINE_ONE_TIME: case VisitorPasswordEnum.OFFLINE_ONE_TIME:
return this.addOfflineOneTimeTemporaryPassword( result = await this.addOfflineOneTimeTemporaryPassword(
addDoorLockTemporaryPasswordDto, addDoorLockTemporaryPasswordDto,
userUuid, userUuid,
projectUuid,
); );
break;
case VisitorPasswordEnum.OFFLINE_MULTIPLE_TIME: case VisitorPasswordEnum.OFFLINE_MULTIPLE_TIME:
return this.addOfflineMultipleTimeTemporaryPassword( result = await this.addOfflineMultipleTimeTemporaryPassword(
addDoorLockTemporaryPasswordDto, addDoorLockTemporaryPasswordDto,
userUuid, userUuid,
projectUuid,
); );
break;
default: default:
throw new BadRequestException('Invalid operation type'); throw new BadRequestException('Invalid operation type');
} }
return new SuccessResponseDto({
message: `Successfully created new ${operationType.toLowerCase()} visitor password`,
data: result,
statusCode: HttpStatus.CREATED,
});
} }
async addOfflineMultipleTimeTemporaryPassword( async addOfflineMultipleTimeTemporaryPassword(
addDoorLockOfflineMultipleDto: AddDoorLockTemporaryPasswordDto, addDoorLockOfflineMultipleDto: AddDoorLockTemporaryPasswordDto,
userUuid: string, userUuid: string,
projectUuid: string,
) { ) {
try { try {
const deviceResults = await Promise.allSettled( const deviceResults = await Promise.allSettled(
addDoorLockOfflineMultipleDto.devicesUuid.map(async (deviceUuid) => { addDoorLockOfflineMultipleDto.devicesUuid.map(async (deviceUuid) => {
try { try {
const deviceDetails = await this.getDeviceByDeviceUuid(deviceUuid); const deviceDetails = await this.getDeviceByDeviceUuid(
deviceUuid,
true,
projectUuid,
);
if (!deviceDetails || !deviceDetails.deviceTuyaUuid) { if (!deviceDetails || !deviceDetails.deviceTuyaUuid) {
throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND);
@ -210,12 +234,17 @@ export class VisitorPasswordService {
async addOfflineOneTimeTemporaryPassword( async addOfflineOneTimeTemporaryPassword(
addDoorLockOfflineOneTimeDto: AddDoorLockTemporaryPasswordDto, addDoorLockOfflineOneTimeDto: AddDoorLockTemporaryPasswordDto,
userUuid: string, userUuid: string,
projectUuid: string,
) { ) {
try { try {
const deviceResults = await Promise.allSettled( const deviceResults = await Promise.allSettled(
addDoorLockOfflineOneTimeDto.devicesUuid.map(async (deviceUuid) => { addDoorLockOfflineOneTimeDto.devicesUuid.map(async (deviceUuid) => {
try { try {
const deviceDetails = await this.getDeviceByDeviceUuid(deviceUuid); const deviceDetails = await this.getDeviceByDeviceUuid(
deviceUuid,
true,
projectUuid,
);
if (!deviceDetails || !deviceDetails.deviceTuyaUuid) { if (!deviceDetails || !deviceDetails.deviceTuyaUuid) {
throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND);
@ -361,6 +390,7 @@ export class VisitorPasswordService {
async addOnlineTemporaryPasswordMultipleTime( async addOnlineTemporaryPasswordMultipleTime(
addDoorLockOnlineMultipleDto: AddDoorLockTemporaryPasswordDto, addDoorLockOnlineMultipleDto: AddDoorLockTemporaryPasswordDto,
userUuid: string, userUuid: string,
projectUuid: string,
) { ) {
try { try {
const deviceResults = await Promise.allSettled( const deviceResults = await Promise.allSettled(
@ -369,6 +399,7 @@ export class VisitorPasswordService {
const passwordData = await this.getTicketAndEncryptedPassword( const passwordData = await this.getTicketAndEncryptedPassword(
deviceUuid, deviceUuid,
addDoorLockOnlineMultipleDto.password, addDoorLockOnlineMultipleDto.password,
projectUuid,
); );
if ( if (
@ -486,6 +517,7 @@ export class VisitorPasswordService {
prodType: ProductType.DL, prodType: ProductType.DL,
}, },
spaceDevice: { spaceDevice: {
spaceName: Not(ORPHAN_SPACE_NAME),
community: { community: {
project: { project: {
uuid: projectUuid, uuid: projectUuid,
@ -495,6 +527,7 @@ export class VisitorPasswordService {
isActive: true, isActive: true,
}, },
}); });
const data = []; const data = [];
deviceIds.forEach((deviceId) => { deviceIds.forEach((deviceId) => {
data.push( data.push(
@ -524,62 +557,21 @@ export class VisitorPasswordService {
.catch(() => {}), .catch(() => {}),
); );
}); });
return (await Promise.all(data)).flat().filter((datum) => { const result = (await Promise.all(data)).flat().filter((datum) => {
return datum != null; return datum != null;
}); });
}
async getAllPassDevices(projectUuid: string) { return new SuccessResponseDto({
await this.validateProject(projectUuid); message: 'Successfully retrieved temporary passwords',
data: result,
const devices = await this.deviceRepository.find({ statusCode: HttpStatus.OK,
where: {
productDevice: {
prodType: ProductType.DL,
},
spaceDevice: {
community: {
project: {
uuid: projectUuid,
},
},
},
isActive: true,
},
relations: ['productDevice', 'spaceDevice'],
}); });
const devicesData = await Promise.all(
devices?.map(async (device) => {
try {
const deviceDetails =
await this.deviceService.getDeviceDetailsByDeviceIdTuya(
device.deviceTuyaUuid,
);
return {
productUuid: device.productDevice.uuid,
productType: device.productDevice.prodType,
...deviceDetails,
uuid: device.uuid,
spaceName: device.spaceDevice.spaceName,
} as GetDeviceDetailsInterface;
} catch (error) {
console.error(
`Error fetching details for device ${device.deviceTuyaUuid}:`,
error,
);
// Return null or a specific value to indicate the error
return null;
}
}),
);
// Filter out null values to only include successful device data
return devicesData.filter((deviceData) => deviceData !== null);
} }
async addOnlineTemporaryPasswordOneTime( async addOnlineTemporaryPasswordOneTime(
addDoorLockOnlineOneTimeDto: AddDoorLockTemporaryPasswordDto, addDoorLockOnlineOneTimeDto: AddDoorLockTemporaryPasswordDto,
userUuid: string, userUuid: string,
projectUuid: string,
) { ) {
try { try {
const deviceResults = await Promise.allSettled( const deviceResults = await Promise.allSettled(
@ -588,6 +580,7 @@ export class VisitorPasswordService {
const passwordData = await this.getTicketAndEncryptedPassword( const passwordData = await this.getTicketAndEncryptedPassword(
deviceUuid, deviceUuid,
addDoorLockOnlineOneTimeDto.password, addDoorLockOnlineOneTimeDto.password,
projectUuid,
); );
if ( if (
@ -698,9 +691,14 @@ export class VisitorPasswordService {
async getTicketAndEncryptedPassword( async getTicketAndEncryptedPassword(
doorLockUuid: string, doorLockUuid: string,
passwordPlan: string, passwordPlan: string,
projectUuid: string,
) { ) {
try { try {
const deviceDetails = await this.getDeviceByDeviceUuid(doorLockUuid); const deviceDetails = await this.getDeviceByDeviceUuid(
doorLockUuid,
true,
projectUuid,
);
if (!deviceDetails || !deviceDetails.deviceTuyaUuid) { if (!deviceDetails || !deviceDetails.deviceTuyaUuid) {
throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND);
@ -919,10 +917,19 @@ export class VisitorPasswordService {
async getDeviceByDeviceUuid( async getDeviceByDeviceUuid(
deviceUuid: string, deviceUuid: string,
withProductDevice: boolean = true, withProductDevice: boolean = true,
projectUuid: string,
) { ) {
try { try {
return await this.deviceRepository.findOne({ return await this.deviceRepository.findOne({
where: { where: {
spaceDevice: {
spaceName: Not(ORPHAN_SPACE_NAME),
community: {
project: {
uuid: projectUuid,
},
},
},
uuid: deviceUuid, uuid: deviceUuid,
isActive: true, isActive: true,
}, },