mirror of
https://github.com/SyncrowIOT/backend.git
synced 2025-07-15 18:27:05 +00:00
Merge branch 'SP-205-be-user-device-permission' into SP-201-be-handle-roles
This commit is contained in:
@ -6,16 +6,14 @@ import { UserEntity } from '../modules/user/entities/user.entity';
|
||||
import { UserSessionEntity } from '../modules/session/entities/session.entity';
|
||||
import { UserOtpEntity } from '../modules/user-otp/entities';
|
||||
import { ProductEntity } from '../modules/product/entities';
|
||||
import {
|
||||
DeviceEntity,
|
||||
DeviceUserPermissionEntity,
|
||||
} from '../modules/device/entities';
|
||||
import { DeviceEntity } from '../modules/device/entities';
|
||||
import { PermissionTypeEntity } from '../modules/permission/entities';
|
||||
import { SpaceEntity } from '../modules/space/entities';
|
||||
import { SpaceTypeEntity } from '../modules/space-type/entities';
|
||||
import { UserSpaceEntity } from '../modules/user-space/entities';
|
||||
import { GroupEntity } from '../modules/group/entities';
|
||||
import { GroupDeviceEntity } from '../modules/group-device/entities';
|
||||
import { DeviceUserPermissionEntity } from '../modules/device-user-permission/entities';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@ -43,6 +41,7 @@ import { GroupDeviceEntity } from '../modules/group-device/entities';
|
||||
UserSpaceEntity,
|
||||
GroupEntity,
|
||||
GroupDeviceEntity,
|
||||
DeviceUserPermissionEntity,
|
||||
],
|
||||
namingStrategy: new SnakeNamingStrategy(),
|
||||
synchronize: Boolean(JSON.parse(configService.get('DB_SYNC'))),
|
||||
|
@ -0,0 +1,10 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { DeviceUserPermissionEntity } from './entities';
|
||||
@Module({
|
||||
providers: [],
|
||||
exports: [],
|
||||
controllers: [],
|
||||
imports: [TypeOrmModule.forFeature([DeviceUserPermissionEntity])],
|
||||
})
|
||||
export class DeviceUserPermissionRepositoryModule {}
|
@ -1,6 +1,6 @@
|
||||
import { IsNotEmpty, IsString } from 'class-validator';
|
||||
|
||||
export class DeviceUserTypeDto {
|
||||
export class DeviceUserPermissionDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
public uuid: string;
|
@ -0,0 +1 @@
|
||||
export * from './device.user.permission.dto';
|
@ -0,0 +1,43 @@
|
||||
import { Column, Entity, ManyToOne, Unique } from 'typeorm';
|
||||
import { AbstractEntity } from '../../abstract/entities/abstract.entity';
|
||||
import { DeviceUserPermissionDto } from '../dtos';
|
||||
import { PermissionTypeEntity } from '../../permission/entities';
|
||||
import { DeviceEntity } from '../../device/entities';
|
||||
import { UserEntity } from '../../user/entities';
|
||||
|
||||
@Entity({ name: 'device-user-permission' })
|
||||
@Unique(['userUuid', 'deviceUuid'])
|
||||
export class DeviceUserPermissionEntity extends AbstractEntity<DeviceUserPermissionDto> {
|
||||
@Column({
|
||||
nullable: false,
|
||||
})
|
||||
public userUuid: string;
|
||||
|
||||
@Column({
|
||||
nullable: false,
|
||||
})
|
||||
deviceUuid: string;
|
||||
|
||||
@ManyToOne(() => DeviceEntity, (device) => device.permission, {
|
||||
nullable: false,
|
||||
})
|
||||
device: DeviceEntity;
|
||||
|
||||
@ManyToOne(
|
||||
() => PermissionTypeEntity,
|
||||
(permissionType) => permissionType.permission,
|
||||
{
|
||||
nullable: false,
|
||||
},
|
||||
)
|
||||
permissionType: PermissionTypeEntity;
|
||||
|
||||
@ManyToOne(() => UserEntity, (user) => user.userPermission, {
|
||||
nullable: false,
|
||||
})
|
||||
user: UserEntity;
|
||||
constructor(partial: Partial<DeviceUserPermissionEntity>) {
|
||||
super();
|
||||
Object.assign(this, partial);
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
export * from './device.user.permission.entity';
|
@ -3,7 +3,7 @@ import { Injectable } from '@nestjs/common';
|
||||
import { DeviceUserPermissionEntity } from '../entities';
|
||||
|
||||
@Injectable()
|
||||
export class DeviceUserTypeRepository extends Repository<DeviceUserPermissionEntity> {
|
||||
export class DeviceUserPermissionRepository extends Repository<DeviceUserPermissionEntity> {
|
||||
constructor(private dataSource: DataSource) {
|
||||
super(DeviceUserPermissionEntity, dataSource.createEntityManager());
|
||||
}
|
@ -0,0 +1 @@
|
||||
export * from './device.user.permission.repository';
|
@ -1,13 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { DeviceEntity, DeviceUserPermissionEntity } from './entities';
|
||||
import { DeviceEntity } from './entities';
|
||||
|
||||
@Module({
|
||||
providers: [],
|
||||
exports: [],
|
||||
controllers: [],
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([DeviceEntity, DeviceUserPermissionEntity]),
|
||||
],
|
||||
imports: [TypeOrmModule.forFeature([DeviceEntity])],
|
||||
})
|
||||
export class DeviceRepositoryModule {}
|
||||
|
@ -1,2 +1 @@
|
||||
export * from './device.dto';
|
||||
export * from './device-user-type.dto';
|
||||
|
@ -1,42 +0,0 @@
|
||||
import { Column, Entity, JoinColumn, ManyToOne } from 'typeorm';
|
||||
import { AbstractEntity } from '../../abstract/entities/abstract.entity';
|
||||
import { DeviceUserTypeDto } from '../dtos/device-user-type.dto';
|
||||
import { DeviceEntity } from './device.entity';
|
||||
import { PermissionTypeEntity } from '../../permission/entities';
|
||||
|
||||
@Entity({ name: 'device-user-permission' })
|
||||
export class DeviceUserPermissionEntity extends AbstractEntity<DeviceUserTypeDto> {
|
||||
@Column({
|
||||
nullable: false,
|
||||
})
|
||||
public userUuid: string;
|
||||
|
||||
@Column({
|
||||
nullable: false,
|
||||
})
|
||||
deviceUuid: string;
|
||||
|
||||
@Column({
|
||||
nullable: false,
|
||||
})
|
||||
public permissionTypeUuid: string;
|
||||
|
||||
@ManyToOne(() => DeviceEntity, {
|
||||
onDelete: 'CASCADE',
|
||||
onUpdate: 'CASCADE',
|
||||
})
|
||||
@JoinColumn({ name: 'device_uuid', referencedColumnName: 'uuid' })
|
||||
device: DeviceEntity;
|
||||
|
||||
@ManyToOne(() => PermissionTypeEntity, {
|
||||
onDelete: 'CASCADE',
|
||||
onUpdate: 'CASCADE',
|
||||
})
|
||||
@JoinColumn({ name: 'permission_type_uuid', referencedColumnName: 'uuid' })
|
||||
type: PermissionTypeEntity;
|
||||
|
||||
constructor(partial: Partial<DeviceUserPermissionEntity>) {
|
||||
super();
|
||||
Object.assign(this, partial);
|
||||
}
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
import { Column, Entity, ManyToOne, OneToMany, Unique } from 'typeorm';
|
||||
import { AbstractEntity } from '../../abstract/entities/abstract.entity';
|
||||
import { DeviceDto } from '../dtos/device.dto';
|
||||
import { DeviceUserPermissionEntity } from './device-user-type.entity';
|
||||
import { GroupDeviceEntity } from '../../group-device/entities';
|
||||
import { SpaceEntity } from '../../space/entities';
|
||||
import { ProductEntity } from '../../product/entities';
|
||||
import { DeviceUserPermissionEntity } from '../../device-user-permission/entities';
|
||||
|
||||
@Entity({ name: 'device' })
|
||||
@Unique(['spaceDevice', 'deviceTuyaUuid'])
|
||||
|
@ -1,2 +1 @@
|
||||
export * from './device.entity';
|
||||
export * from './device-user-type.entity';
|
||||
|
@ -1,2 +1 @@
|
||||
export * from './device.repository';
|
||||
export * from './device-user-type.repository';
|
||||
|
@ -2,7 +2,7 @@ import { Column, Entity, OneToMany } from 'typeorm';
|
||||
import { AbstractEntity } from '../../abstract/entities/abstract.entity';
|
||||
import { PermissionType } from '@app/common/constants/permission-type.enum';
|
||||
import { PermissionTypeDto } from '../dtos/permission.dto';
|
||||
import { DeviceUserPermissionEntity } from '../../device/entities';
|
||||
import { DeviceUserPermissionEntity } from '../../device-user-permission/entities';
|
||||
|
||||
@Entity({ name: 'permission-type' })
|
||||
export class PermissionTypeEntity extends AbstractEntity<PermissionTypeDto> {
|
||||
@ -14,7 +14,7 @@ export class PermissionTypeEntity extends AbstractEntity<PermissionTypeDto> {
|
||||
|
||||
@OneToMany(
|
||||
() => DeviceUserPermissionEntity,
|
||||
(permission) => permission.type,
|
||||
(permission) => permission.permissionType,
|
||||
{
|
||||
nullable: true,
|
||||
onDelete: 'CASCADE',
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { DeviceUserPermissionEntity } from '../../device-user-permission/entities/device.user.permission.entity';
|
||||
import { Column, Entity, OneToMany } from 'typeorm';
|
||||
import { UserDto } from '../dtos';
|
||||
import { AbstractEntity } from '../../abstract/entities/abstract.entity';
|
||||
@ -51,6 +52,11 @@ export class UserEntity extends AbstractEntity<UserDto> {
|
||||
@OneToMany(() => UserSpaceEntity, (userSpace) => userSpace.user)
|
||||
userSpaces: UserSpaceEntity[];
|
||||
|
||||
@OneToMany(
|
||||
() => DeviceUserPermissionEntity,
|
||||
(userPermission) => userPermission.user,
|
||||
)
|
||||
userPermission: DeviceUserPermissionEntity[];
|
||||
constructor(partial: Partial<UserEntity>) {
|
||||
super();
|
||||
Object.assign(this, partial);
|
||||
|
@ -9,6 +9,7 @@ import {
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
UseGuards,
|
||||
Req,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
|
||||
import {
|
||||
@ -23,6 +24,8 @@ import { ControlDeviceDto } from '../dtos/control.device.dto';
|
||||
import { CheckRoomGuard } from 'src/guards/room.guard';
|
||||
import { CheckGroupGuard } from 'src/guards/group.guard';
|
||||
import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard';
|
||||
import { CheckUserHavePermission } from 'src/guards/user.device.permission.guard';
|
||||
import { CheckUserHaveControllablePermission } from 'src/guards/user.device.controllable.permission.guard';
|
||||
|
||||
@ApiTags('Device Module')
|
||||
@Controller({
|
||||
@ -37,10 +40,13 @@ export class DeviceController {
|
||||
@Get('room')
|
||||
async getDevicesByRoomId(
|
||||
@Query() getDeviceByRoomUuidDto: GetDeviceByRoomUuidDto,
|
||||
@Req() req: any,
|
||||
) {
|
||||
try {
|
||||
const userUuid = req.user.uuid;
|
||||
return await this.deviceService.getDevicesByRoomId(
|
||||
getDeviceByRoomUuidDto,
|
||||
userUuid,
|
||||
);
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
@ -68,10 +74,13 @@ export class DeviceController {
|
||||
@Get('group')
|
||||
async getDevicesByGroupId(
|
||||
@Query() getDeviceByGroupIdDto: GetDeviceByGroupIdDto,
|
||||
@Req() req: any,
|
||||
) {
|
||||
try {
|
||||
const userUuid = req.user.uuid;
|
||||
return await this.deviceService.getDevicesByGroupId(
|
||||
getDeviceByGroupIdDto,
|
||||
userUuid,
|
||||
);
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
@ -94,11 +103,18 @@ export class DeviceController {
|
||||
}
|
||||
}
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@UseGuards(JwtAuthGuard, CheckUserHavePermission)
|
||||
@Get(':deviceUuid')
|
||||
async getDeviceDetailsByDeviceId(@Param('deviceUuid') deviceUuid: string) {
|
||||
async getDeviceDetailsByDeviceId(
|
||||
@Param('deviceUuid') deviceUuid: string,
|
||||
@Req() req: any,
|
||||
) {
|
||||
try {
|
||||
return await this.deviceService.getDeviceDetailsByDeviceId(deviceUuid);
|
||||
const userUuid = req.user.uuid;
|
||||
return await this.deviceService.getDeviceDetailsByDeviceId(
|
||||
deviceUuid,
|
||||
userUuid,
|
||||
);
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
error.message || 'Internal server error',
|
||||
@ -107,7 +123,7 @@ export class DeviceController {
|
||||
}
|
||||
}
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@UseGuards(JwtAuthGuard, CheckUserHavePermission)
|
||||
@Get(':deviceUuid/functions')
|
||||
async getDeviceInstructionByDeviceId(
|
||||
@Param('deviceUuid') deviceUuid: string,
|
||||
@ -124,7 +140,7 @@ export class DeviceController {
|
||||
}
|
||||
}
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@UseGuards(JwtAuthGuard, CheckUserHavePermission)
|
||||
@Get(':deviceUuid/functions/status')
|
||||
async getDevicesInstructionStatus(@Param('deviceUuid') deviceUuid: string) {
|
||||
try {
|
||||
@ -138,11 +154,17 @@ export class DeviceController {
|
||||
}
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Post('control')
|
||||
async controlDevice(@Body() controlDeviceDto: ControlDeviceDto) {
|
||||
@UseGuards(JwtAuthGuard, CheckUserHaveControllablePermission)
|
||||
@Post(':deviceUuid/control')
|
||||
async controlDevice(
|
||||
@Body() controlDeviceDto: ControlDeviceDto,
|
||||
@Param('deviceUuid') deviceUuid: string,
|
||||
) {
|
||||
try {
|
||||
return await this.deviceService.controlDevice(controlDeviceDto);
|
||||
return await this.deviceService.controlDevice(
|
||||
controlDeviceDto,
|
||||
deviceUuid,
|
||||
);
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
error.message || 'Internal server error',
|
||||
|
@ -5,15 +5,14 @@ import { ConfigModule } from '@nestjs/config';
|
||||
import { ProductRepositoryModule } from '@app/common/modules/product/product.repository.module';
|
||||
import { ProductRepository } from '@app/common/modules/product/repositories';
|
||||
import { DeviceRepositoryModule } from '@app/common/modules/device';
|
||||
import {
|
||||
DeviceRepository,
|
||||
DeviceUserTypeRepository,
|
||||
} from '@app/common/modules/device/repositories';
|
||||
import { DeviceRepository } from '@app/common/modules/device/repositories';
|
||||
import { PermissionTypeRepository } from '@app/common/modules/permission/repositories';
|
||||
import { SpaceRepository } from '@app/common/modules/space/repositories';
|
||||
import { GroupDeviceRepository } from '@app/common/modules/group-device/repositories';
|
||||
import { GroupRepository } from '@app/common/modules/group/repositories';
|
||||
import { GroupRepositoryModule } from '@app/common/modules/group/group.repository.module';
|
||||
import { DeviceUserPermissionRepository } from '@app/common/modules/device-user-permission/repositories';
|
||||
import { UserRepository } from '@app/common/modules/user/repositories';
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule,
|
||||
@ -25,12 +24,13 @@ import { GroupRepositoryModule } from '@app/common/modules/group/group.repositor
|
||||
providers: [
|
||||
DeviceService,
|
||||
ProductRepository,
|
||||
DeviceUserTypeRepository,
|
||||
DeviceUserPermissionRepository,
|
||||
PermissionTypeRepository,
|
||||
SpaceRepository,
|
||||
DeviceRepository,
|
||||
GroupDeviceRepository,
|
||||
GroupRepository,
|
||||
UserRepository,
|
||||
],
|
||||
exports: [DeviceService],
|
||||
})
|
||||
|
@ -2,14 +2,6 @@ import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty, IsString } from 'class-validator';
|
||||
|
||||
export class ControlDeviceDto {
|
||||
@ApiProperty({
|
||||
description: 'deviceUuid',
|
||||
required: true,
|
||||
})
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
public deviceUuid: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'code',
|
||||
required: true,
|
||||
|
@ -26,6 +26,7 @@ import { ControlDeviceDto } from '../dtos/control.device.dto';
|
||||
import { convertKeysToCamelCase } from '@app/common/helper/camelCaseConverter';
|
||||
import { DeviceRepository } from '@app/common/modules/device/repositories';
|
||||
import { GroupDeviceRepository } from '@app/common/modules/group-device/repositories';
|
||||
import { PermissionType } from '@app/common/constants/permission-type.enum';
|
||||
|
||||
@Injectable()
|
||||
export class DeviceService {
|
||||
@ -47,13 +48,25 @@ export class DeviceService {
|
||||
|
||||
async getDevicesByRoomId(
|
||||
getDeviceByRoomUuidDto: GetDeviceByRoomUuidDto,
|
||||
userUuid: string,
|
||||
): Promise<GetDeviceDetailsInterface[]> {
|
||||
try {
|
||||
const devices = await this.deviceRepository.find({
|
||||
where: {
|
||||
spaceDevice: { uuid: getDeviceByRoomUuidDto.roomUuid },
|
||||
permission: {
|
||||
userUuid,
|
||||
permissionType: {
|
||||
type: PermissionType.READ || PermissionType.CONTROLLABLE,
|
||||
},
|
||||
},
|
||||
},
|
||||
relations: ['spaceDevice', 'productDevice'],
|
||||
relations: [
|
||||
'spaceDevice',
|
||||
'productDevice',
|
||||
'permission',
|
||||
'permission.permissionType',
|
||||
],
|
||||
});
|
||||
const devicesData = await Promise.all(
|
||||
devices.map(async (device) => {
|
||||
@ -64,6 +77,7 @@ export class DeviceService {
|
||||
uuid: device.uuid,
|
||||
productUuid: device.productDevice.uuid,
|
||||
productType: device.productDevice.prodType,
|
||||
permissionType: device.permission[0].permissionType.type,
|
||||
} as GetDeviceDetailsInterface;
|
||||
}),
|
||||
);
|
||||
@ -77,11 +91,29 @@ export class DeviceService {
|
||||
}
|
||||
}
|
||||
|
||||
async getDevicesByGroupId(getDeviceByGroupIdDto: GetDeviceByGroupIdDto) {
|
||||
async getDevicesByGroupId(
|
||||
getDeviceByGroupIdDto: GetDeviceByGroupIdDto,
|
||||
userUuid: string,
|
||||
) {
|
||||
try {
|
||||
const groupDevices = await this.groupDeviceRepository.find({
|
||||
where: { group: { uuid: getDeviceByGroupIdDto.groupUuid } },
|
||||
relations: ['device'],
|
||||
where: {
|
||||
group: { uuid: getDeviceByGroupIdDto.groupUuid },
|
||||
device: {
|
||||
permission: {
|
||||
userUuid,
|
||||
permissionType: {
|
||||
type: PermissionType.READ || PermissionType.CONTROLLABLE,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
relations: [
|
||||
'device',
|
||||
'device.productDevice',
|
||||
'device.permission',
|
||||
'device.permission.permissionType',
|
||||
],
|
||||
});
|
||||
const devicesData = await Promise.all(
|
||||
groupDevices.map(async (device) => {
|
||||
@ -89,9 +121,10 @@ export class DeviceService {
|
||||
...(await this.getDeviceDetailsByDeviceIdTuya(
|
||||
device.device.deviceTuyaUuid,
|
||||
)),
|
||||
uuid: device.uuid,
|
||||
uuid: device.device.uuid,
|
||||
productUuid: device.device.productDevice.uuid,
|
||||
productType: device.device.productDevice.prodType,
|
||||
permissionType: device.device.permission[0].permissionType.type,
|
||||
} as GetDeviceDetailsInterface;
|
||||
}),
|
||||
);
|
||||
@ -104,7 +137,6 @@ export class DeviceService {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async addDeviceInRoom(addDeviceInRoomDto: AddDeviceInRoomDto) {
|
||||
try {
|
||||
const device = await this.getDeviceDetailsByDeviceIdTuya(
|
||||
@ -158,11 +190,11 @@ export class DeviceService {
|
||||
}
|
||||
}
|
||||
|
||||
async controlDevice(controlDeviceDto: ControlDeviceDto) {
|
||||
async controlDevice(controlDeviceDto: ControlDeviceDto, deviceUuid: string) {
|
||||
try {
|
||||
const deviceDetails = await this.deviceRepository.findOne({
|
||||
where: {
|
||||
uuid: controlDeviceDto.deviceUuid,
|
||||
uuid: deviceUuid,
|
||||
},
|
||||
});
|
||||
|
||||
@ -183,7 +215,10 @@ export class DeviceService {
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND);
|
||||
throw new HttpException(
|
||||
error.message || 'Device Not Found',
|
||||
error.status || HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
}
|
||||
async controlDeviceTuya(
|
||||
@ -211,12 +246,18 @@ export class DeviceService {
|
||||
}
|
||||
}
|
||||
|
||||
async getDeviceDetailsByDeviceId(deviceUuid: string) {
|
||||
async getDeviceDetailsByDeviceId(deviceUuid: string, userUuid: string) {
|
||||
try {
|
||||
const userDevicePermission = await this.getUserDevicePermission(
|
||||
userUuid,
|
||||
deviceUuid,
|
||||
);
|
||||
|
||||
const deviceDetails = await this.deviceRepository.findOne({
|
||||
where: {
|
||||
uuid: deviceUuid,
|
||||
},
|
||||
relations: ['productDevice'],
|
||||
});
|
||||
|
||||
if (!deviceDetails) {
|
||||
@ -232,9 +273,13 @@ export class DeviceService {
|
||||
uuid: deviceDetails.uuid,
|
||||
productUuid: deviceDetails.productDevice.uuid,
|
||||
productType: deviceDetails.productDevice.prodType,
|
||||
permissionType: userDevicePermission,
|
||||
};
|
||||
} catch (error) {
|
||||
throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND);
|
||||
throw new HttpException(
|
||||
error.message || 'Device Not Found',
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
}
|
||||
async getDeviceDetailsByDeviceIdTuya(
|
||||
@ -257,6 +302,7 @@ export class DeviceService {
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { productName, productId, ...rest } = camelCaseResponse.result;
|
||||
|
||||
return {
|
||||
...rest,
|
||||
productUuid: product.uuid,
|
||||
@ -370,4 +416,13 @@ export class DeviceService {
|
||||
);
|
||||
}
|
||||
}
|
||||
private async getUserDevicePermission(userUuid: string, deviceUuid: string) {
|
||||
const device = await this.deviceRepository.findOne({
|
||||
where: {
|
||||
uuid: deviceUuid,
|
||||
},
|
||||
relations: ['permission', 'permission.permissionType'],
|
||||
});
|
||||
return device.permission[0].permissionType.type;
|
||||
}
|
||||
}
|
||||
|
@ -1,97 +0,0 @@
|
||||
import { PermissionType } from '@app/common/constants/permission-type.enum';
|
||||
import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard';
|
||||
import { DeviceUserTypeRepository } from '@app/common/modules/device/repositories';
|
||||
import { PermissionTypeRepository } from '@app/common/modules/permission/repositories';
|
||||
import {
|
||||
Injectable,
|
||||
CanActivate,
|
||||
HttpStatus,
|
||||
ExecutionContext,
|
||||
BadRequestException,
|
||||
applyDecorators,
|
||||
SetMetadata,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import { Reflector } from '@nestjs/core';
|
||||
|
||||
@Injectable()
|
||||
export class DevicePermissionGuard implements CanActivate {
|
||||
constructor(
|
||||
private reflector: Reflector,
|
||||
private readonly deviceUserTypeRepository: DeviceUserTypeRepository,
|
||||
private readonly permissionTypeRepository: PermissionTypeRepository,
|
||||
) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const req = context.switchToHttp().getRequest();
|
||||
try {
|
||||
const { deviceId } = req.headers;
|
||||
const userId = req.user.uuid;
|
||||
|
||||
const requirePermission =
|
||||
this.reflector.getAllAndOverride<PermissionType>('permission', [
|
||||
context.getHandler(),
|
||||
context.getClass(),
|
||||
]);
|
||||
|
||||
if (!requirePermission) {
|
||||
return true;
|
||||
}
|
||||
await this.checkDevicePermission(deviceId, userId, requirePermission);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
this.handleGuardError(error, context);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async checkDevicePermission(
|
||||
deviceId: string,
|
||||
userId: string,
|
||||
requirePermission,
|
||||
) {
|
||||
const [userPermissionDetails, permissionDetails] = await Promise.all([
|
||||
this.deviceUserTypeRepository.findOne({
|
||||
where: { deviceUuid: deviceId, userUuid: userId },
|
||||
}),
|
||||
this.permissionTypeRepository.findOne({
|
||||
where: {
|
||||
type: requirePermission,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
if (!userPermissionDetails) {
|
||||
throw new BadRequestException('User Permission Details Not Found');
|
||||
}
|
||||
if (userPermissionDetails.permissionTypeUuid !== permissionDetails.uuid) {
|
||||
throw new BadRequestException(
|
||||
`User Does not have a ${requirePermission}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private handleGuardError(error: Error, context: ExecutionContext) {
|
||||
const response = context.switchToHttp().getResponse();
|
||||
console.error(error);
|
||||
|
||||
if (error instanceof BadRequestException) {
|
||||
response
|
||||
.status(HttpStatus.BAD_REQUEST)
|
||||
.json({ statusCode: HttpStatus.BAD_REQUEST, message: error.message });
|
||||
} else {
|
||||
response.status(HttpStatus.NOT_FOUND).json({
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
message: 'User Permission not found',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function AuthGuardWithRoles(permission?: string) {
|
||||
return applyDecorators(
|
||||
SetMetadata('permission', permission),
|
||||
UseGuards(JwtAuthGuard),
|
||||
UseGuards(DevicePermissionGuard),
|
||||
);
|
||||
}
|
92
src/guards/user.device.controllable.permission.guard.ts
Normal file
92
src/guards/user.device.controllable.permission.guard.ts
Normal file
@ -0,0 +1,92 @@
|
||||
import {
|
||||
CanActivate,
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
HttpStatus,
|
||||
} from '@nestjs/common';
|
||||
import { BadRequestException, NotFoundException } from '@nestjs/common';
|
||||
import { UserRepository } from '@app/common/modules/user/repositories';
|
||||
import { DeviceRepository } from '@app/common/modules/device/repositories';
|
||||
import { PermissionType } from '@app/common/constants/permission-type.enum';
|
||||
|
||||
@Injectable()
|
||||
export class CheckUserHaveControllablePermission implements CanActivate {
|
||||
constructor(
|
||||
private readonly deviceRepository: DeviceRepository,
|
||||
private readonly userRepository: UserRepository,
|
||||
) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const req = context.switchToHttp().getRequest();
|
||||
|
||||
try {
|
||||
const userUuid = req.user.uuid;
|
||||
const { deviceUuid } = req.params;
|
||||
|
||||
const userIsFound = await this.checkUserIsFound(userUuid);
|
||||
if (!userIsFound) {
|
||||
throw new NotFoundException('User not found');
|
||||
}
|
||||
|
||||
const userDevicePermission = await this.checkUserDevicePermission(
|
||||
userUuid,
|
||||
deviceUuid,
|
||||
);
|
||||
|
||||
if (userDevicePermission === PermissionType.CONTROLLABLE) {
|
||||
return true;
|
||||
} else {
|
||||
throw new BadRequestException(
|
||||
'You do not have controllable access to this device',
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
this.handleGuardError(error, context);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private async checkUserIsFound(userUuid: string) {
|
||||
const userData = await this.userRepository.findOne({
|
||||
where: { uuid: userUuid },
|
||||
});
|
||||
return !!userData;
|
||||
}
|
||||
|
||||
private async checkUserDevicePermission(
|
||||
userUuid: string,
|
||||
deviceUuid: string,
|
||||
): Promise<string> {
|
||||
const device = await this.deviceRepository.findOne({
|
||||
where: { uuid: deviceUuid, permission: { userUuid: userUuid } },
|
||||
relations: ['permission', 'permission.permissionType'],
|
||||
});
|
||||
|
||||
if (!device) {
|
||||
throw new BadRequestException(
|
||||
'You do not have controllable access to this device',
|
||||
);
|
||||
}
|
||||
|
||||
return device.permission[0].permissionType.type; // Assuming permissionType is a string
|
||||
}
|
||||
|
||||
private handleGuardError(error: Error, context: ExecutionContext) {
|
||||
const response = context.switchToHttp().getResponse();
|
||||
if (error instanceof NotFoundException) {
|
||||
response
|
||||
.status(HttpStatus.NOT_FOUND)
|
||||
.json({ statusCode: HttpStatus.NOT_FOUND, message: error.message });
|
||||
} else if (error instanceof BadRequestException) {
|
||||
response.status(HttpStatus.BAD_REQUEST).json({
|
||||
statusCode: HttpStatus.BAD_REQUEST,
|
||||
message: error.message,
|
||||
});
|
||||
} else {
|
||||
response.status(HttpStatus.INTERNAL_SERVER_ERROR).json({
|
||||
statusCode: HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
message: error.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
91
src/guards/user.device.permission.guard.ts
Normal file
91
src/guards/user.device.permission.guard.ts
Normal file
@ -0,0 +1,91 @@
|
||||
import {
|
||||
CanActivate,
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
HttpStatus,
|
||||
} from '@nestjs/common';
|
||||
import { BadRequestException, NotFoundException } from '@nestjs/common';
|
||||
import { UserRepository } from '@app/common/modules/user/repositories';
|
||||
import { DeviceRepository } from '@app/common/modules/device/repositories';
|
||||
import { PermissionType } from '@app/common/constants/permission-type.enum';
|
||||
|
||||
@Injectable()
|
||||
export class CheckUserHavePermission implements CanActivate {
|
||||
constructor(
|
||||
private readonly deviceRepository: DeviceRepository,
|
||||
private readonly userRepository: UserRepository,
|
||||
) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const req = context.switchToHttp().getRequest();
|
||||
|
||||
try {
|
||||
const userUuid = req.user.uuid;
|
||||
const { deviceUuid } = req.params;
|
||||
|
||||
const userIsFound = await this.checkUserIsFound(userUuid);
|
||||
if (!userIsFound) {
|
||||
throw new NotFoundException('User not found');
|
||||
}
|
||||
|
||||
const userDevicePermission = await this.checkUserDevicePermission(
|
||||
userUuid,
|
||||
deviceUuid,
|
||||
);
|
||||
|
||||
if (
|
||||
userDevicePermission === PermissionType.READ ||
|
||||
userDevicePermission === PermissionType.CONTROLLABLE
|
||||
) {
|
||||
return true;
|
||||
} else {
|
||||
throw new BadRequestException('You do not have access to this device');
|
||||
}
|
||||
} catch (error) {
|
||||
this.handleGuardError(error, context);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private async checkUserIsFound(userUuid: string) {
|
||||
const userData = await this.userRepository.findOne({
|
||||
where: { uuid: userUuid },
|
||||
});
|
||||
return !!userData;
|
||||
}
|
||||
|
||||
private async checkUserDevicePermission(
|
||||
userUuid: string,
|
||||
deviceUuid: string,
|
||||
): Promise<string> {
|
||||
const device = await this.deviceRepository.findOne({
|
||||
where: { uuid: deviceUuid, permission: { userUuid: userUuid } },
|
||||
relations: ['permission', 'permission.permissionType'],
|
||||
});
|
||||
|
||||
if (!device) {
|
||||
throw new BadRequestException('You do not have access to this device');
|
||||
}
|
||||
|
||||
return device.permission[0].permissionType.type; // Assuming permissionType is a string
|
||||
}
|
||||
|
||||
private handleGuardError(error: Error, context: ExecutionContext) {
|
||||
const response = context.switchToHttp().getResponse();
|
||||
if (error instanceof NotFoundException) {
|
||||
response
|
||||
.status(HttpStatus.NOT_FOUND)
|
||||
.json({ statusCode: HttpStatus.NOT_FOUND, message: error.message });
|
||||
} else if (error instanceof BadRequestException) {
|
||||
response.status(HttpStatus.BAD_REQUEST).json({
|
||||
statusCode: HttpStatus.BAD_REQUEST,
|
||||
message: error.message,
|
||||
});
|
||||
} else {
|
||||
response.status(HttpStatus.INTERNAL_SERVER_ERROR).json({
|
||||
statusCode: HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
message: error.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
export * from './user-device-permission.controller';
|
||||
|
@ -1,7 +1,9 @@
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Delete,
|
||||
Get,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
Param,
|
||||
Post,
|
||||
@ -40,40 +42,45 @@ export class UserDevicePermissionController {
|
||||
message: 'User Permission for Devices Added Successfully',
|
||||
data: addDetails,
|
||||
};
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
error.message || 'Internal server error',
|
||||
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Put('edit/:userId')
|
||||
@Put('edit/:devicePermissionUuid')
|
||||
async editDevicePermission(
|
||||
@Param('userId') userId: string,
|
||||
@Param('devicePermissionUuid') devicePermissionUuid: string,
|
||||
@Body() userDevicePermissionEditDto: UserDevicePermissionEditDto,
|
||||
) {
|
||||
try {
|
||||
await this.userDevicePermissionService.editUserPermission(
|
||||
userId,
|
||||
devicePermissionUuid,
|
||||
userDevicePermissionEditDto,
|
||||
);
|
||||
return {
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'User Permission for Devices Updated Successfully',
|
||||
data: {},
|
||||
};
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
error.message || 'Internal server error',
|
||||
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Get('list')
|
||||
async fetchDevicePermission() {
|
||||
@Get(':deviceUuid/list')
|
||||
async fetchDevicePermission(@Param('deviceUuid') deviceUuid: string) {
|
||||
try {
|
||||
const deviceDetails =
|
||||
await this.userDevicePermissionService.fetchuserPermission();
|
||||
await this.userDevicePermissionService.fetchUserPermission(deviceUuid);
|
||||
return {
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'Device Details fetched Successfully',
|
||||
@ -83,4 +90,25 @@ export class UserDevicePermissionController {
|
||||
throw new Error(err);
|
||||
}
|
||||
}
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Delete(':devicePermissionUuid')
|
||||
async deleteDevicePermission(
|
||||
@Param('devicePermissionUuid') devicePermissionUuid: string,
|
||||
) {
|
||||
try {
|
||||
await this.userDevicePermissionService.deleteDevicePermission(
|
||||
devicePermissionUuid,
|
||||
);
|
||||
return {
|
||||
statusCode: HttpStatus.OK,
|
||||
message: 'User Permission for Devices Deleted Successfully',
|
||||
};
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
error.message || 'Internal server error',
|
||||
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,2 @@
|
||||
export * from './user-device-permission.add.dto';
|
||||
export * from './user-device-permission.edit.dto';
|
||||
|
@ -1,28 +1,29 @@
|
||||
import { PermissionType } from '@app/common/constants/permission-type.enum';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty, IsString } from 'class-validator';
|
||||
import { IsEnum, IsNotEmpty, IsString } from 'class-validator';
|
||||
|
||||
export class UserDevicePermissionAddDto {
|
||||
@ApiProperty({
|
||||
description: 'user id',
|
||||
description: 'user uuid',
|
||||
required: true,
|
||||
})
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
userId: string;
|
||||
userUuid: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'permission type id',
|
||||
description: 'permission type',
|
||||
enum: PermissionType,
|
||||
required: true,
|
||||
})
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
permissionTypeId: string;
|
||||
@IsEnum(PermissionType)
|
||||
permissionType: PermissionType;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'device id',
|
||||
description: 'device uuid',
|
||||
required: true,
|
||||
})
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
deviceId: string;
|
||||
deviceUuid: string;
|
||||
}
|
||||
|
@ -1,7 +1,13 @@
|
||||
import { OmitType } from '@nestjs/swagger';
|
||||
import { UserDevicePermissionAddDto } from './user-device-permission.add.dto';
|
||||
import { PermissionType } from '@app/common/constants/permission-type.enum';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsEnum } from 'class-validator';
|
||||
|
||||
export class UserDevicePermissionEditDto extends OmitType(
|
||||
UserDevicePermissionAddDto,
|
||||
['userId'],
|
||||
) {}
|
||||
export class UserDevicePermissionEditDto {
|
||||
@ApiProperty({
|
||||
description: 'permission type',
|
||||
enum: PermissionType,
|
||||
required: true,
|
||||
})
|
||||
@IsEnum(PermissionType)
|
||||
permissionType: PermissionType;
|
||||
}
|
||||
|
@ -0,0 +1 @@
|
||||
export * from './user-device-permission.service';
|
||||
|
@ -1,36 +1,107 @@
|
||||
import { DeviceUserTypeRepository } from '@app/common/modules/device/repositories';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||
import { UserDevicePermissionAddDto } from '../dtos/user-device-permission.add.dto';
|
||||
import { UserDevicePermissionEditDto } from '../dtos/user-device-permission.edit.dto';
|
||||
import { DeviceUserPermissionRepository } from '@app/common/modules/device-user-permission/repositories';
|
||||
import { PermissionTypeRepository } from '@app/common/modules/permission/repositories';
|
||||
|
||||
@Injectable()
|
||||
export class UserDevicePermissionService {
|
||||
constructor(
|
||||
private readonly deviceUserTypeRepository: DeviceUserTypeRepository,
|
||||
private readonly deviceUserPermissionRepository: DeviceUserPermissionRepository,
|
||||
private readonly permissionTypeRepository: PermissionTypeRepository,
|
||||
) {}
|
||||
|
||||
async addUserPermission(userDevicePermissionDto: UserDevicePermissionAddDto) {
|
||||
return await this.deviceUserTypeRepository.save({
|
||||
userUuid: userDevicePermissionDto.userId,
|
||||
deviceUuid: userDevicePermissionDto.deviceId,
|
||||
permissionTypeUuid: userDevicePermissionDto.permissionTypeId,
|
||||
});
|
||||
try {
|
||||
const permissionType = await this.getPermissionType(
|
||||
userDevicePermissionDto.permissionType,
|
||||
);
|
||||
return await this.deviceUserPermissionRepository.save({
|
||||
userUuid: userDevicePermissionDto.userUuid,
|
||||
deviceUuid: userDevicePermissionDto.deviceUuid,
|
||||
permissionType: {
|
||||
uuid: permissionType.uuid,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
if (error.code === '23505') {
|
||||
throw new HttpException(
|
||||
'This User already belongs to this device',
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
throw new HttpException(
|
||||
error.message || 'Internal Server Error',
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async editUserPermission(
|
||||
userId: string,
|
||||
devicePermissionUuid: string,
|
||||
userDevicePermissionEditDto: UserDevicePermissionEditDto,
|
||||
) {
|
||||
return await this.deviceUserTypeRepository.update(
|
||||
{ userUuid: userId },
|
||||
{
|
||||
deviceUuid: userDevicePermissionEditDto.deviceId,
|
||||
permissionTypeUuid: userDevicePermissionEditDto.permissionTypeId,
|
||||
},
|
||||
);
|
||||
try {
|
||||
const permissionType = await this.getPermissionType(
|
||||
userDevicePermissionEditDto.permissionType,
|
||||
);
|
||||
return await this.deviceUserPermissionRepository.update(
|
||||
{ uuid: devicePermissionUuid },
|
||||
{
|
||||
permissionType: {
|
||||
uuid: permissionType.uuid,
|
||||
},
|
||||
},
|
||||
);
|
||||
} catch (error) {
|
||||
if (error.code === '23505') {
|
||||
throw new HttpException(
|
||||
'This User already belongs to this device',
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
throw new HttpException(
|
||||
error.message || 'Internal Server Error',
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async fetchuserPermission() {
|
||||
return await this.deviceUserTypeRepository.find();
|
||||
async fetchUserPermission(deviceUuid: string) {
|
||||
const devicePermissions = await this.deviceUserPermissionRepository.find({
|
||||
where: {
|
||||
deviceUuid: deviceUuid,
|
||||
},
|
||||
relations: ['permissionType', 'user'],
|
||||
});
|
||||
return devicePermissions.map((permission) => {
|
||||
return {
|
||||
uuid: permission.uuid,
|
||||
deviceUuid: permission.deviceUuid,
|
||||
firstName: permission.user.firstName,
|
||||
lastName: permission.user.lastName,
|
||||
email: permission.user.email,
|
||||
permissionType: permission.permissionType.type,
|
||||
};
|
||||
});
|
||||
}
|
||||
private async getPermissionType(permissionType: string) {
|
||||
return await this.permissionTypeRepository.findOne({
|
||||
where: {
|
||||
type: permissionType,
|
||||
},
|
||||
});
|
||||
}
|
||||
async deleteDevicePermission(devicePermissionUuid: string) {
|
||||
try {
|
||||
return await this.deviceUserPermissionRepository.delete({
|
||||
uuid: devicePermissionUuid,
|
||||
});
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
error.message || 'Internal Server Error',
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,18 @@
|
||||
import { DeviceRepositoryModule } from '@app/common/modules/device';
|
||||
import {
|
||||
DeviceRepository,
|
||||
DeviceUserTypeRepository,
|
||||
} from '@app/common/modules/device/repositories';
|
||||
import { DeviceRepository } from '@app/common/modules/device/repositories';
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { UserDevicePermissionService } from './services/user-device-permission.service';
|
||||
import { UserDevicePermissionController } from './controllers/user-device-permission.controller';
|
||||
import { DeviceUserPermissionRepository } from '@app/common/modules/device-user-permission/repositories';
|
||||
import { PermissionTypeRepository } from '@app/common/modules/permission/repositories';
|
||||
|
||||
@Module({
|
||||
imports: [ConfigModule, DeviceRepositoryModule],
|
||||
controllers: [UserDevicePermissionController],
|
||||
providers: [
|
||||
DeviceUserTypeRepository,
|
||||
DeviceUserPermissionRepository,
|
||||
PermissionTypeRepository,
|
||||
DeviceRepository,
|
||||
UserDevicePermissionService,
|
||||
],
|
||||
|
Reference in New Issue
Block a user