Merge branch 'dev' into SP-197-be-retrieve-devices-in-the-gateway

This commit is contained in:
faris Aljohari
2024-05-21 15:35:31 +03:00
90 changed files with 1546 additions and 314 deletions

View File

@ -4,4 +4,5 @@ export class AuthInterface {
uuid: string; uuid: string;
sessionId: string; sessionId: string;
id: number; id: number;
roles?: string[];
} }

View File

@ -22,7 +22,9 @@ export class AuthService {
where: { where: {
email, email,
}, },
relations: ['roles.roleType'],
}); });
if (!user.isUserVerified) { if (!user.isUserVerified) {
throw new BadRequestException('User is not verified'); throw new BadRequestException('User is not verified');
} }
@ -68,7 +70,9 @@ export class AuthService {
uuid: user.uuid, uuid: user.uuid,
type: user.type, type: user.type,
sessionId: user.sessionId, sessionId: user.sessionId,
roles: user?.roles,
}; };
const tokens = await this.getTokens(payload); const tokens = await this.getTokens(payload);
await this.updateRefreshToken(user.uuid, tokens.refreshToken); await this.updateRefreshToken(user.uuid, tokens.refreshToken);
return tokens; return tokens;

View File

@ -28,9 +28,10 @@ export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
if (validateUser) { if (validateUser) {
return { return {
email: payload.email, email: payload.email,
userId: payload.id, userUuid: payload.uuid,
uuid: payload.uuid, uuid: payload.uuid,
sessionId: payload.sessionId, sessionId: payload.sessionId,
roles: payload?.roles,
}; };
} else { } else {
throw new BadRequestException('Unauthorized'); throw new BadRequestException('Unauthorized');

View File

@ -31,9 +31,10 @@ export class RefreshTokenStrategy extends PassportStrategy(
if (validateUser) { if (validateUser) {
return { return {
email: payload.email, email: payload.email,
userId: payload.id, userUuid: payload.uuid,
uuid: payload.uuid, uuid: payload.uuid,
sessionId: payload.sessionId, sessionId: payload.sessionId,
roles: payload?.roles,
}; };
} else { } else {
throw new BadRequestException('Unauthorized'); throw new BadRequestException('Unauthorized');

View File

@ -1,2 +1,3 @@
import emailConfig from './email.config'; import emailConfig from './email.config';
export default [emailConfig]; import superAdminConfig from './super.admin.config';
export default [emailConfig, superAdminConfig];

View File

@ -0,0 +1,9 @@
import { registerAs } from '@nestjs/config';
export default registerAs(
'super-admin',
(): Record<string, any> => ({
SUPER_ADMIN_EMAIL: process.env.SUPER_ADMIN_EMAIL,
SUPER_ADMIN_PASSWORD: process.env.SUPER_ADMIN_PASSWORD,
}),
);

View File

@ -0,0 +1,4 @@
export enum RoleType {
SUPER_ADMIN = 'SUPER_ADMIN',
ADMIN = 'ADMIN',
}

View File

@ -0,0 +1,7 @@
export enum SpaceType {
COMMUNITY = 'community',
BUILDING = 'building',
FLOOR = 'floor',
UNIT = 'unit',
ROOM = 'room',
}

View File

@ -6,16 +6,16 @@ import { UserEntity } from '../modules/user/entities/user.entity';
import { UserSessionEntity } from '../modules/session/entities/session.entity'; import { UserSessionEntity } from '../modules/session/entities/session.entity';
import { UserOtpEntity } from '../modules/user-otp/entities'; import { UserOtpEntity } from '../modules/user-otp/entities';
import { ProductEntity } from '../modules/product/entities'; import { ProductEntity } from '../modules/product/entities';
import { import { DeviceEntity } from '../modules/device/entities';
DeviceEntity,
DeviceUserPermissionEntity,
} from '../modules/device/entities';
import { PermissionTypeEntity } from '../modules/permission/entities'; import { PermissionTypeEntity } from '../modules/permission/entities';
import { SpaceEntity } from '../modules/space/entities'; import { SpaceEntity } from '../modules/space/entities';
import { SpaceTypeEntity } from '../modules/space-type/entities'; import { SpaceTypeEntity } from '../modules/space-type/entities';
import { UserSpaceEntity } from '../modules/user-space/entities'; import { UserSpaceEntity } from '../modules/user-space/entities';
import { GroupEntity } from '../modules/group/entities'; import { GroupEntity } from '../modules/group/entities';
import { GroupDeviceEntity } from '../modules/group-device/entities'; import { GroupDeviceEntity } from '../modules/group-device/entities';
import { DeviceUserPermissionEntity } from '../modules/device-user-permission/entities';
import { UserRoleEntity } from '../modules/user-role/entities';
import { RoleTypeEntity } from '../modules/role-type/entities';
@Module({ @Module({
imports: [ imports: [
@ -43,6 +43,9 @@ import { GroupDeviceEntity } from '../modules/group-device/entities';
UserSpaceEntity, UserSpaceEntity,
GroupEntity, GroupEntity,
GroupDeviceEntity, GroupDeviceEntity,
DeviceUserPermissionEntity,
UserRoleEntity,
RoleTypeEntity,
], ],
namingStrategy: new SnakeNamingStrategy(), namingStrategy: new SnakeNamingStrategy(),
synchronize: Boolean(JSON.parse(configService.get('DB_SYNC'))), synchronize: Boolean(JSON.parse(configService.get('DB_SYNC'))),

View File

@ -1,11 +1,14 @@
import { Global, Module } from '@nestjs/common'; import { Global, Module } from '@nestjs/common';
import { HelperHashService } from './services'; import { HelperHashService } from './services';
import { SpacePermissionService } from './services/space.permission.service';
import { SpaceRepository } from '../modules/space/repositories';
import { SpaceRepositoryModule } from '../modules/space/space.repository.module';
@Global() @Global()
@Module({ @Module({
providers: [HelperHashService], providers: [HelperHashService, SpacePermissionService, SpaceRepository],
exports: [HelperHashService], exports: [HelperHashService, SpacePermissionService],
controllers: [], controllers: [],
imports: [], imports: [SpaceRepositoryModule],
}) })
export class HelperModule {} export class HelperModule {}

View File

@ -1 +1,2 @@
export * from './helper.hash.service'; export * from './helper.hash.service';
export * from './space.permission.service';

View File

@ -0,0 +1,39 @@
import { Injectable } from '@nestjs/common';
import { SpaceRepository } from '@app/common/modules/space/repositories';
import { BadRequestException } from '@nestjs/common';
@Injectable()
export class SpacePermissionService {
constructor(private readonly spaceRepository: SpaceRepository) {}
async checkUserPermission(
spaceUuid: string,
userUuid: string,
type: string,
): Promise<void> {
try {
const spaceData = await this.spaceRepository.findOne({
where: {
uuid: spaceUuid,
spaceType: {
type: type,
},
userSpaces: {
user: {
uuid: userUuid,
},
},
},
relations: ['spaceType', 'userSpaces', 'userSpaces.user'],
});
if (!spaceData) {
throw new BadRequestException(
`You do not have permission to access this ${type}`,
);
}
} catch (err) {
throw new BadRequestException(err.message || 'Invalid UUID');
}
}
}

View File

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

View File

@ -1,6 +1,6 @@
import { IsNotEmpty, IsString } from 'class-validator'; import { IsNotEmpty, IsString } from 'class-validator';
export class DeviceUserTypeDto { export class DeviceUserPermissionDto {
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
public uuid: string; public uuid: string;

View File

@ -0,0 +1 @@
export * from './device.user.permission.dto';

View File

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

View File

@ -0,0 +1 @@
export * from './device.user.permission.entity';

View File

@ -3,7 +3,7 @@ import { Injectable } from '@nestjs/common';
import { DeviceUserPermissionEntity } from '../entities'; import { DeviceUserPermissionEntity } from '../entities';
@Injectable() @Injectable()
export class DeviceUserTypeRepository extends Repository<DeviceUserPermissionEntity> { export class DeviceUserPermissionRepository extends Repository<DeviceUserPermissionEntity> {
constructor(private dataSource: DataSource) { constructor(private dataSource: DataSource) {
super(DeviceUserPermissionEntity, dataSource.createEntityManager()); super(DeviceUserPermissionEntity, dataSource.createEntityManager());
} }

View File

@ -0,0 +1 @@
export * from './device.user.permission.repository';

View File

@ -1,13 +1,11 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm'; import { TypeOrmModule } from '@nestjs/typeorm';
import { DeviceEntity, DeviceUserPermissionEntity } from './entities'; import { DeviceEntity } from './entities';
@Module({ @Module({
providers: [], providers: [],
exports: [], exports: [],
controllers: [], controllers: [],
imports: [ imports: [TypeOrmModule.forFeature([DeviceEntity])],
TypeOrmModule.forFeature([DeviceEntity, DeviceUserPermissionEntity]),
],
}) })
export class DeviceRepositoryModule {} export class DeviceRepositoryModule {}

View File

@ -1,2 +1 @@
export * from './device.dto'; export * from './device.dto';
export * from './device-user-type.dto';

View File

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

View File

@ -1,10 +1,10 @@
import { Column, Entity, ManyToOne, OneToMany, Unique } from 'typeorm'; import { Column, Entity, ManyToOne, OneToMany, Unique } from 'typeorm';
import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { AbstractEntity } from '../../abstract/entities/abstract.entity';
import { DeviceDto } from '../dtos/device.dto'; import { DeviceDto } from '../dtos/device.dto';
import { DeviceUserPermissionEntity } from './device-user-type.entity';
import { GroupDeviceEntity } from '../../group-device/entities'; import { GroupDeviceEntity } from '../../group-device/entities';
import { SpaceEntity } from '../../space/entities'; import { SpaceEntity } from '../../space/entities';
import { ProductEntity } from '../../product/entities'; import { ProductEntity } from '../../product/entities';
import { DeviceUserPermissionEntity } from '../../device-user-permission/entities';
@Entity({ name: 'device' }) @Entity({ name: 'device' })
@Unique(['spaceDevice', 'deviceTuyaUuid']) @Unique(['spaceDevice', 'deviceTuyaUuid'])
@ -25,8 +25,6 @@ export class DeviceEntity extends AbstractEntity<DeviceDto> {
(permission) => permission.device, (permission) => permission.device,
{ {
nullable: true, nullable: true,
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
}, },
) )
permission: DeviceUserPermissionEntity[]; permission: DeviceUserPermissionEntity[];

View File

@ -1,2 +1 @@
export * from './device.entity'; export * from './device.entity';
export * from './device-user-type.entity';

View File

@ -1,2 +1 @@
export * from './device.repository'; export * from './device.repository';
export * from './device-user-type.repository';

View File

@ -19,7 +19,6 @@ export class GroupEntity extends AbstractEntity<GroupDto> {
@OneToMany(() => GroupDeviceEntity, (groupDevice) => groupDevice.group, { @OneToMany(() => GroupDeviceEntity, (groupDevice) => groupDevice.group, {
cascade: true, cascade: true,
onDelete: 'CASCADE',
}) })
groupDevices: GroupDeviceEntity[]; groupDevices: GroupDeviceEntity[];

View File

@ -2,7 +2,7 @@ import { Column, Entity, OneToMany } from 'typeorm';
import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { AbstractEntity } from '../../abstract/entities/abstract.entity';
import { PermissionType } from '@app/common/constants/permission-type.enum'; import { PermissionType } from '@app/common/constants/permission-type.enum';
import { PermissionTypeDto } from '../dtos/permission.dto'; import { PermissionTypeDto } from '../dtos/permission.dto';
import { DeviceUserPermissionEntity } from '../../device/entities'; import { DeviceUserPermissionEntity } from '../../device-user-permission/entities';
@Entity({ name: 'permission-type' }) @Entity({ name: 'permission-type' })
export class PermissionTypeEntity extends AbstractEntity<PermissionTypeDto> { export class PermissionTypeEntity extends AbstractEntity<PermissionTypeDto> {
@ -14,11 +14,9 @@ export class PermissionTypeEntity extends AbstractEntity<PermissionTypeDto> {
@OneToMany( @OneToMany(
() => DeviceUserPermissionEntity, () => DeviceUserPermissionEntity,
(permission) => permission.type, (permission) => permission.permissionType,
{ {
nullable: true, nullable: true,
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
}, },
) )
permission: DeviceUserPermissionEntity[]; permission: DeviceUserPermissionEntity[];

View File

@ -0,0 +1 @@
export * from './role.type.dto';

View File

@ -0,0 +1,11 @@
import { RoleType } from '@app/common/constants/role.type.enum';
import { IsEnum, IsNotEmpty, IsString } from 'class-validator';
export class RoleTypeDto {
@IsString()
@IsNotEmpty()
public uuid: string;
@IsEnum(RoleType)
public type: RoleType;
}

View File

@ -0,0 +1 @@
export * from './role.type.entity';

View File

@ -0,0 +1,23 @@
import { Column, Entity, OneToMany, Unique } from 'typeorm';
import { AbstractEntity } from '../../abstract/entities/abstract.entity';
import { RoleTypeDto } from '../dtos/role.type.dto';
import { RoleType } from '@app/common/constants/role.type.enum';
import { UserRoleEntity } from '../../user-role/entities';
@Entity({ name: 'role-type' })
@Unique(['type'])
export class RoleTypeEntity extends AbstractEntity<RoleTypeDto> {
@Column({
nullable: false,
enum: Object.values(RoleType),
})
type: string;
@OneToMany(() => UserRoleEntity, (role) => role.roleType, {
nullable: true,
})
roles: UserRoleEntity[];
constructor(partial: Partial<RoleTypeEntity>) {
super();
Object.assign(this, partial);
}
}

View File

@ -0,0 +1 @@
export * from './role.type.repository';

View File

@ -0,0 +1,10 @@
import { DataSource, Repository } from 'typeorm';
import { Injectable } from '@nestjs/common';
import { RoleTypeEntity } from '../entities/role.type.entity';
@Injectable()
export class RoleTypeRepository extends Repository<RoleTypeEntity> {
constructor(private dataSource: DataSource) {
super(RoleTypeEntity, dataSource.createEntityManager());
}
}

View File

@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { RoleTypeEntity } from './entities/role.type.entity';
@Module({
providers: [],
exports: [],
controllers: [],
imports: [TypeOrmModule.forFeature([RoleTypeEntity])],
})
export class RoleTypeRepositoryModule {}

View File

@ -0,0 +1 @@
export * from './user.role.dto';

View File

@ -0,0 +1,15 @@
import { IsNotEmpty, IsString } from 'class-validator';
export class UserRoleDto {
@IsString()
@IsNotEmpty()
public uuid: string;
@IsString()
@IsNotEmpty()
public userUuid: string;
@IsString()
@IsNotEmpty()
public roleTypeUuid: string;
}

View File

@ -0,0 +1 @@
export * from './user.role.entity';

View File

@ -0,0 +1,24 @@
import { Entity, ManyToOne, Unique } from 'typeorm';
import { AbstractEntity } from '../../abstract/entities/abstract.entity';
import { UserRoleDto } from '../dtos';
import { UserEntity } from '../../user/entities';
import { RoleTypeEntity } from '../../role-type/entities';
@Entity({ name: 'user-role' })
@Unique(['user', 'roleType'])
export class UserRoleEntity extends AbstractEntity<UserRoleDto> {
@ManyToOne(() => UserEntity, (user) => user.roles, {
nullable: false,
})
user: UserEntity;
@ManyToOne(() => RoleTypeEntity, (roleType) => roleType.roles, {
nullable: false,
})
roleType: RoleTypeEntity;
constructor(partial: Partial<UserRoleEntity>) {
super();
Object.assign(this, partial);
}
}

View File

@ -0,0 +1 @@
export * from './user.role.repository';

View File

@ -0,0 +1,10 @@
import { DataSource, Repository } from 'typeorm';
import { Injectable } from '@nestjs/common';
import { UserRoleEntity } from '../entities';
@Injectable()
export class UserRoleRepository extends Repository<UserRoleEntity> {
constructor(private dataSource: DataSource) {
super(UserRoleEntity, dataSource.createEntityManager());
}
}

View File

@ -0,0 +1,10 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UserRoleEntity } from './entities';
@Module({
providers: [],
exports: [],
controllers: [],
imports: [TypeOrmModule.forFeature([UserRoleEntity])],
})
export class UserRoleRepositoryModule {}

View File

@ -1,7 +1,9 @@
import { DeviceUserPermissionEntity } from '../../device-user-permission/entities/device.user.permission.entity';
import { Column, Entity, OneToMany } from 'typeorm'; import { Column, Entity, OneToMany } from 'typeorm';
import { UserDto } from '../dtos'; import { UserDto } from '../dtos';
import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { AbstractEntity } from '../../abstract/entities/abstract.entity';
import { UserSpaceEntity } from '../../user-space/entities'; import { UserSpaceEntity } from '../../user-space/entities';
import { UserRoleEntity } from '../../user-role/entities';
@Entity({ name: 'user' }) @Entity({ name: 'user' })
export class UserEntity extends AbstractEntity<UserDto> { export class UserEntity extends AbstractEntity<UserDto> {
@ -51,6 +53,16 @@ export class UserEntity extends AbstractEntity<UserDto> {
@OneToMany(() => UserSpaceEntity, (userSpace) => userSpace.user) @OneToMany(() => UserSpaceEntity, (userSpace) => userSpace.user)
userSpaces: UserSpaceEntity[]; userSpaces: UserSpaceEntity[];
@OneToMany(
() => DeviceUserPermissionEntity,
(userPermission) => userPermission.user,
)
userPermission: DeviceUserPermissionEntity[];
@OneToMany(() => UserRoleEntity, (role) => role.user, {
nullable: true,
})
roles: UserRoleEntity[];
constructor(partial: Partial<UserEntity>) { constructor(partial: Partial<UserEntity>) {
super(); super();
Object.assign(this, partial); Object.assign(this, partial);

View File

@ -0,0 +1,43 @@
import { Global, Module } from '@nestjs/common';
import { SeederService } from './services/seeder.service';
import { PermissionTypeRepository } from '../modules/permission/repositories';
import { PermissionTypeSeeder } from './services/permission.type.seeder';
import { PermissionTypeRepositoryModule } from '../modules/permission/permission.repository.module';
import { ConfigModule } from '@nestjs/config';
import { RoleTypeRepositoryModule } from '../modules/role-type/role.type.repository.module';
import { RoleTypeRepository } from '../modules/role-type/repositories';
import { RoleTypeSeeder } from './services/role.type.seeder';
import { SpaceTypeRepository } from '../modules/space-type/repositories';
import { SpaceTypeSeeder } from './services/space.type.seeder';
import { SpaceTypeRepositoryModule } from '../modules/space-type/space.type.repository.module';
import { SuperAdminSeeder } from './services/supper.admin.seeder';
import { UserRepository } from '../modules/user/repositories';
import { UserRoleRepository } from '../modules/user-role/repositories';
import { UserRoleRepositoryModule } from '../modules/user-role/user.role.repository.module';
import { UserRepositoryModule } from '../modules/user/user.repository.module';
@Global()
@Module({
providers: [
PermissionTypeSeeder,
RoleTypeSeeder,
SpaceTypeSeeder,
SeederService,
PermissionTypeRepository,
RoleTypeRepository,
SpaceTypeRepository,
SuperAdminSeeder,
UserRepository,
UserRoleRepository,
],
exports: [SeederService],
controllers: [],
imports: [
ConfigModule.forRoot(),
PermissionTypeRepositoryModule,
RoleTypeRepositoryModule,
UserRepositoryModule,
UserRoleRepositoryModule,
SpaceTypeRepositoryModule,
],
})
export class SeederModule {}

View File

@ -0,0 +1,49 @@
import { Injectable } from '@nestjs/common';
import { PermissionTypeRepository } from '../../modules/permission/repositories';
import { PermissionType } from '../../constants/permission-type.enum';
@Injectable()
export class PermissionTypeSeeder {
constructor(
private readonly permissionTypeRepository: PermissionTypeRepository,
) {}
async addPermissionTypeDataIfNotFound(): Promise<void> {
try {
const existingPermissionTypes =
await this.permissionTypeRepository.find();
const permissionTypeNames = existingPermissionTypes.map((pt) => pt.type);
const missingPermissionTypes = [];
if (!permissionTypeNames.includes(PermissionType.CONTROLLABLE)) {
missingPermissionTypes.push(PermissionType.CONTROLLABLE);
}
if (!permissionTypeNames.includes(PermissionType.READ)) {
missingPermissionTypes.push(PermissionType.READ);
}
if (missingPermissionTypes.length > 0) {
await this.addPermissionTypeData(missingPermissionTypes);
}
} catch (err) {
console.error('Error while checking permission type data:', err);
throw err;
}
}
private async addPermissionTypeData(
permissionTypes: string[],
): Promise<void> {
try {
const permissionTypeEntities = permissionTypes.map((type) => ({
type,
}));
await this.permissionTypeRepository.save(permissionTypeEntities);
} catch (err) {
console.error('Error while adding permission type data:', err);
throw err;
}
}
}

View File

@ -0,0 +1,44 @@
import { Injectable } from '@nestjs/common';
import { RoleType } from '../../constants/role.type.enum';
import { RoleTypeRepository } from '../../modules/role-type/repositories';
@Injectable()
export class RoleTypeSeeder {
constructor(private readonly roleTypeRepository: RoleTypeRepository) {}
async addRoleTypeDataIfNotFound(): Promise<void> {
try {
const existingRoleTypes = await this.roleTypeRepository.find();
const roleTypeNames = existingRoleTypes.map((pt) => pt.type);
const missingRoleTypes = [];
if (!roleTypeNames.includes(RoleType.SUPER_ADMIN)) {
missingRoleTypes.push(RoleType.SUPER_ADMIN);
}
if (!roleTypeNames.includes(RoleType.ADMIN)) {
missingRoleTypes.push(RoleType.ADMIN);
}
if (missingRoleTypes.length > 0) {
await this.addRoleTypeData(missingRoleTypes);
}
} catch (err) {
console.error('Error while checking role type data:', err);
throw err;
}
}
private async addRoleTypeData(roleTypes: string[]): Promise<void> {
try {
const roleTypeEntities = roleTypes.map((type) => ({
type,
}));
await this.roleTypeRepository.save(roleTypeEntities);
} catch (err) {
console.error('Error while adding role type data:', err);
throw err;
}
}
}

View File

@ -0,0 +1,21 @@
import { Injectable } from '@nestjs/common';
import { PermissionTypeSeeder } from './permission.type.seeder';
import { RoleTypeSeeder } from './role.type.seeder';
import { SpaceTypeSeeder } from './space.type.seeder';
import { SuperAdminSeeder } from './supper.admin.seeder';
@Injectable()
export class SeederService {
constructor(
private readonly permissionTypeSeeder: PermissionTypeSeeder,
private readonly roleTypeSeeder: RoleTypeSeeder,
private readonly spaceTypeSeeder: SpaceTypeSeeder,
private readonly superAdminSeeder: SuperAdminSeeder,
) {}
async seed() {
await this.permissionTypeSeeder.addPermissionTypeDataIfNotFound();
await this.roleTypeSeeder.addRoleTypeDataIfNotFound();
await this.spaceTypeSeeder.addSpaceTypeDataIfNotFound();
await this.superAdminSeeder.createSuperAdminIfNotFound();
}
}

View File

@ -0,0 +1,52 @@
import { Injectable } from '@nestjs/common';
import { SpaceType } from '../../constants/space-type.enum';
import { SpaceTypeRepository } from '../../modules/space-type/repositories';
@Injectable()
export class SpaceTypeSeeder {
constructor(private readonly spaceTypeRepository: SpaceTypeRepository) {}
async addSpaceTypeDataIfNotFound(): Promise<void> {
try {
const existingSpaceTypes = await this.spaceTypeRepository.find();
const spaceTypeNames = existingSpaceTypes.map((pt) => pt.type);
const missingSpaceTypes = [];
if (!spaceTypeNames.includes(SpaceType.COMMUNITY)) {
missingSpaceTypes.push(SpaceType.COMMUNITY);
}
if (!spaceTypeNames.includes(SpaceType.BUILDING)) {
missingSpaceTypes.push(SpaceType.BUILDING);
}
if (!spaceTypeNames.includes(SpaceType.FLOOR)) {
missingSpaceTypes.push(SpaceType.FLOOR);
}
if (!spaceTypeNames.includes(SpaceType.UNIT)) {
missingSpaceTypes.push(SpaceType.UNIT);
}
if (!spaceTypeNames.includes(SpaceType.ROOM)) {
missingSpaceTypes.push(SpaceType.ROOM);
}
if (missingSpaceTypes.length > 0) {
await this.addSpaceTypeData(missingSpaceTypes);
}
} catch (err) {
console.error('Error while checking space type data:', err);
throw err;
}
}
private async addSpaceTypeData(spaceTypes: string[]): Promise<void> {
try {
const spaceTypeEntities = spaceTypes.map((type) => ({
type,
}));
await this.spaceTypeRepository.save(spaceTypeEntities);
} catch (err) {
console.error('Error while adding space type data:', err);
throw err;
}
}
}

View File

@ -0,0 +1,72 @@
import { Injectable } from '@nestjs/common';
import { UserRepository } from '@app/common/modules/user/repositories';
import { RoleType } from '@app/common/constants/role.type.enum';
import { UserRoleRepository } from '@app/common/modules/user-role/repositories';
import { RoleTypeRepository } from '@app/common/modules/role-type/repositories';
import { ConfigService } from '@nestjs/config';
import { HelperHashService } from '../../helper/services';
@Injectable()
export class SuperAdminSeeder {
constructor(
private readonly configService: ConfigService,
private readonly userRepository: UserRepository,
private readonly userRoleRepository: UserRoleRepository,
private readonly roleTypeRepository: RoleTypeRepository,
private readonly helperHashService: HelperHashService,
) {}
async createSuperAdminIfNotFound(): Promise<void> {
try {
const superAdminData = await this.userRoleRepository.find({
where: { roleType: { type: RoleType.SUPER_ADMIN } },
relations: ['roleType'],
});
if (superAdminData.length <= 0) {
// Create the super admin user if not found
console.log('Creating super admin user...');
await this.createSuperAdmin();
}
} catch (err) {
console.error('Error while checking super admin:', err);
throw err;
}
}
private async getRoleUuidByRoleType(roleType: string) {
const role = await this.roleTypeRepository.findOne({
where: { type: roleType },
});
return role.uuid;
}
private async createSuperAdmin(): Promise<void> {
const salt = this.helperHashService.randomSalt(10); // Hash the password using bcrypt
const hashedPassword = await this.helperHashService.bcrypt(
this.configService.get<string>('super-admin.SUPER_ADMIN_PASSWORD'),
salt,
);
try {
const user = await this.userRepository.save({
email: this.configService.get<string>('super-admin.SUPER_ADMIN_EMAIL'),
password: hashedPassword,
firstName: 'Super',
lastName: 'Admin',
isUserVerified: true,
isActive: true,
});
const defaultUserRoleUuid = await this.getRoleUuidByRoleType(
RoleType.SUPER_ADMIN,
);
await this.userRoleRepository.save({
user: { uuid: user.uuid },
roleType: { uuid: defaultUserRoleUuid },
});
} catch (err) {
console.error('Error while creating super admin:', err);
throw err;
}
}
}

View File

@ -12,6 +12,8 @@ import { CommunityModule } from './community/community.module';
import { BuildingModule } from './building/building.module'; import { BuildingModule } from './building/building.module';
import { FloorModule } from './floor/floor.module'; import { FloorModule } from './floor/floor.module';
import { UnitModule } from './unit/unit.module'; import { UnitModule } from './unit/unit.module';
import { RoleModule } from './role/role.module';
import { SeederModule } from '@app/common/seed/seeder.module';
@Module({ @Module({
imports: [ imports: [
ConfigModule.forRoot({ ConfigModule.forRoot({
@ -19,6 +21,7 @@ import { UnitModule } from './unit/unit.module';
}), }),
AuthenticationModule, AuthenticationModule,
UserModule, UserModule,
RoleModule,
CommunityModule, CommunityModule,
BuildingModule, BuildingModule,
FloorModule, FloorModule,
@ -28,6 +31,7 @@ import { UnitModule } from './unit/unit.module';
GroupModule, GroupModule,
DeviceModule, DeviceModule,
UserDevicePermissionModule, UserDevicePermissionModule,
SeederModule,
], ],
controllers: [AuthenticationController], controllers: [AuthenticationController],
}) })

View File

@ -9,6 +9,8 @@ import { UserAuthService } from './services';
import { UserRepository } from '../../libs/common/src/modules/user/repositories'; import { UserRepository } from '../../libs/common/src/modules/user/repositories';
import { UserSessionRepository } from '../../libs/common/src/modules/session/repositories/session.repository'; import { UserSessionRepository } from '../../libs/common/src/modules/session/repositories/session.repository';
import { UserOtpRepository } from '../../libs/common/src/modules/user-otp/repositories/user-otp.repository'; import { UserOtpRepository } from '../../libs/common/src/modules/user-otp/repositories/user-otp.repository';
import { UserRoleRepository } from '@app/common/modules/user-role/repositories';
import { RoleTypeRepository } from '@app/common/modules/role-type/repositories';
@Module({ @Module({
imports: [ConfigModule, UserRepositoryModule, CommonModule], imports: [ConfigModule, UserRepositoryModule, CommonModule],
@ -19,6 +21,8 @@ import { UserOtpRepository } from '../../libs/common/src/modules/user-otp/reposi
UserRepository, UserRepository,
UserSessionRepository, UserSessionRepository,
UserOtpRepository, UserOtpRepository,
UserRoleRepository,
RoleTypeRepository,
], ],
exports: [AuthenticationService, UserAuthService], exports: [AuthenticationService, UserAuthService],
}) })

View File

@ -14,9 +14,9 @@ import { UserSignUpDto } from '../dtos/user-auth.dto';
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
import { ResponseMessage } from '../../../libs/common/src/response/response.decorator'; import { ResponseMessage } from '../../../libs/common/src/response/response.decorator';
import { UserLoginDto } from '../dtos/user-login.dto'; import { UserLoginDto } from '../dtos/user-login.dto';
import { JwtAuthGuard } from '../../../libs/common/src/guards/jwt.auth.guard';
import { ForgetPasswordDto, UserOtpDto, VerifyOtpDto } from '../dtos'; import { ForgetPasswordDto, UserOtpDto, VerifyOtpDto } from '../dtos';
import { RefreshTokenGuard } from '@app/common/guards/jwt-refresh.auth.guard'; import { RefreshTokenGuard } from '@app/common/guards/jwt-refresh.auth.guard';
import { SuperAdminRoleGuard } from 'src/guards/super.admin.role.guard';
@Controller({ @Controller({
version: '1', version: '1',
@ -47,12 +47,12 @@ export class UserAuthController {
return { return {
statusCode: HttpStatus.CREATED, statusCode: HttpStatus.CREATED,
data: accessToken, data: accessToken,
message: 'User Loggedin Successfully', message: 'User Logged in Successfully',
}; };
} }
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(JwtAuthGuard) @UseGuards(SuperAdminRoleGuard)
@Delete('user/delete/:id') @Delete('user/delete/:id')
async userDelete(@Param('id') id: string) { async userDelete(@Param('id') id: string) {
await this.userAuthService.deleteUser(id); await this.userAuthService.deleteUser(id);
@ -98,7 +98,7 @@ export class UserAuthController {
} }
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(JwtAuthGuard) @UseGuards(SuperAdminRoleGuard)
@Get('user/list') @Get('user/list')
async userList() { async userList() {
const userList = await this.userAuthService.userList(); const userList = await this.userAuthService.userList();

View File

@ -1,3 +1,5 @@
import { RoleTypeRepository } from './../../../libs/common/src/modules/role-type/repositories/role.type.repository';
import { UserRoleRepository } from './../../../libs/common/src/modules/user-role/repositories/user.role.repository';
import { UserRepository } from '../../../libs/common/src/modules/user/repositories'; import { UserRepository } from '../../../libs/common/src/modules/user/repositories';
import { import {
BadRequestException, BadRequestException,
@ -26,6 +28,8 @@ export class UserAuthService {
private readonly helperHashService: HelperHashService, private readonly helperHashService: HelperHashService,
private readonly authService: AuthService, private readonly authService: AuthService,
private readonly emailService: EmailService, private readonly emailService: EmailService,
private readonly userRoleRepository: UserRoleRepository,
private readonly roleTypeRepository: RoleTypeRepository,
) {} ) {}
async signUp(userSignUpDto: UserSignUpDto): Promise<UserEntity> { async signUp(userSignUpDto: UserSignUpDto): Promise<UserEntity> {
@ -33,12 +37,22 @@ export class UserAuthService {
if (findUser) { if (findUser) {
throw new BadRequestException('User already registered with given email'); throw new BadRequestException('User already registered with given email');
} }
const salt = this.helperHashService.randomSalt(10); const salt = this.helperHashService.randomSalt(10); // Hash the password using bcrypt
const password = this.helperHashService.bcrypt( const hashedPassword = await this.helperHashService.bcrypt(
userSignUpDto.password, userSignUpDto.password,
salt, salt,
); );
return await this.userRepository.save({ ...userSignUpDto, password });
try {
const user = await this.userRepository.save({
...userSignUpDto,
password: hashedPassword,
});
return user;
} catch (error) {
throw new BadRequestException('Failed to register user');
}
} }
async findUser(email: string) { async findUser(email: string) {
@ -67,6 +81,7 @@ export class UserAuthService {
async userLogin(data: UserLoginDto) { async userLogin(data: UserLoginDto) {
const user = await this.authService.validateUser(data.email, data.password); const user = await this.authService.validateUser(data.email, data.password);
if (!user) { if (!user) {
throw new UnauthorizedException('Invalid login credentials.'); throw new UnauthorizedException('Invalid login credentials.');
} }
@ -89,6 +104,9 @@ export class UserAuthService {
email: user.email, email: user.email,
userId: user.uuid, userId: user.uuid,
uuid: user.uuid, uuid: user.uuid,
roles: user?.roles?.map((role) => {
return { uuid: role.uuid, type: role.roleType.type };
}),
sessionId: session[1].uuid, sessionId: session[1].uuid,
}); });
} }

View File

@ -12,12 +12,14 @@ import {
UseGuards, UseGuards,
} from '@nestjs/common'; } from '@nestjs/common';
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
import { JwtAuthGuard } from '../../../libs/common/src/guards/jwt.auth.guard';
import { AddBuildingDto, AddUserBuildingDto } from '../dtos/add.building.dto'; import { AddBuildingDto, AddUserBuildingDto } from '../dtos/add.building.dto';
import { GetBuildingChildDto } from '../dtos/get.building.dto'; import { GetBuildingChildDto } from '../dtos/get.building.dto';
import { UpdateBuildingNameDto } from '../dtos/update.building.dto'; import { UpdateBuildingNameDto } from '../dtos/update.building.dto';
import { CheckCommunityTypeGuard } from 'src/guards/community.type.guard'; import { CheckCommunityTypeGuard } from 'src/guards/community.type.guard';
import { CheckUserBuildingGuard } from 'src/guards/user.building.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';
@ApiTags('Building Module') @ApiTags('Building Module')
@Controller({ @Controller({
@ -28,12 +30,17 @@ export class BuildingController {
constructor(private readonly buildingService: BuildingService) {} constructor(private readonly buildingService: BuildingService) {}
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(JwtAuthGuard, CheckCommunityTypeGuard) @UseGuards(AdminRoleGuard, CheckCommunityTypeGuard)
@Post() @Post()
async addBuilding(@Body() addBuildingDto: AddBuildingDto) { async addBuilding(@Body() addBuildingDto: AddBuildingDto) {
try { try {
const building = await this.buildingService.addBuilding(addBuildingDto); const building = await this.buildingService.addBuilding(addBuildingDto);
return { message: 'Building added successfully', uuid: building.uuid }; return {
statusCode: HttpStatus.CREATED,
success: true,
message: 'Building added successfully',
data: building,
};
} catch (error) { } catch (error) {
throw new HttpException( throw new HttpException(
error.message || 'Internal server error', error.message || 'Internal server error',
@ -43,7 +50,7 @@ export class BuildingController {
} }
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard, BuildingPermissionGuard)
@Get(':buildingUuid') @Get(':buildingUuid')
async getBuildingByUuid(@Param('buildingUuid') buildingUuid: string) { async getBuildingByUuid(@Param('buildingUuid') buildingUuid: string) {
try { try {
@ -59,7 +66,7 @@ export class BuildingController {
} }
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard, BuildingPermissionGuard)
@Get('child/:buildingUuid') @Get('child/:buildingUuid')
async getBuildingChildByUuid( async getBuildingChildByUuid(
@Param('buildingUuid') buildingUuid: string, @Param('buildingUuid') buildingUuid: string,
@ -79,7 +86,7 @@ export class BuildingController {
} }
} }
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard, BuildingPermissionGuard)
@Get('parent/:buildingUuid') @Get('parent/:buildingUuid')
async getBuildingParentByUuid(@Param('buildingUuid') buildingUuid: string) { async getBuildingParentByUuid(@Param('buildingUuid') buildingUuid: string) {
try { try {
@ -94,12 +101,16 @@ export class BuildingController {
} }
} }
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(JwtAuthGuard, CheckUserBuildingGuard) @UseGuards(AdminRoleGuard, CheckUserBuildingGuard)
@Post('user') @Post('user')
async addUserBuilding(@Body() addUserBuildingDto: AddUserBuildingDto) { async addUserBuilding(@Body() addUserBuildingDto: AddUserBuildingDto) {
try { try {
await this.buildingService.addUserBuilding(addUserBuildingDto); await this.buildingService.addUserBuilding(addUserBuildingDto);
return { message: 'user building added successfully' }; return {
statusCode: HttpStatus.CREATED,
success: true,
message: 'user building added successfully',
};
} catch (error) { } catch (error) {
throw new HttpException( throw new HttpException(
error.message || 'Internal server error', error.message || 'Internal server error',
@ -122,7 +133,7 @@ export class BuildingController {
} }
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard, BuildingPermissionGuard)
@Put('rename/:buildingUuid') @Put('rename/:buildingUuid')
async renameBuildingByUuid( async renameBuildingByUuid(
@Param('buildingUuid') buildingUuid: string, @Param('buildingUuid') buildingUuid: string,

View File

@ -10,6 +10,7 @@ import { UserSpaceRepositoryModule } from '@app/common/modules/user-space/user.s
import { UserSpaceRepository } from '@app/common/modules/user-space/repositories'; import { UserSpaceRepository } from '@app/common/modules/user-space/repositories';
import { UserRepositoryModule } from '@app/common/modules/user/user.repository.module'; import { UserRepositoryModule } from '@app/common/modules/user/user.repository.module';
import { UserRepository } from '@app/common/modules/user/repositories'; import { UserRepository } from '@app/common/modules/user/repositories';
import { SpacePermissionService } from '@app/common/helper/services';
@Module({ @Module({
imports: [ imports: [
@ -26,7 +27,8 @@ import { UserRepository } from '@app/common/modules/user/repositories';
SpaceTypeRepository, SpaceTypeRepository,
UserSpaceRepository, UserSpaceRepository,
UserRepository, UserRepository,
SpacePermissionService,
], ],
exports: [CommunityService], exports: [CommunityService, SpacePermissionService],
}) })
export class CommunityModule {} export class CommunityModule {}

View File

@ -12,7 +12,6 @@ import {
UseGuards, UseGuards,
} from '@nestjs/common'; } from '@nestjs/common';
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
import { JwtAuthGuard } from '../../../libs/common/src/guards/jwt.auth.guard';
import { import {
AddCommunityDto, AddCommunityDto,
AddUserCommunityDto, AddUserCommunityDto,
@ -20,6 +19,9 @@ import {
import { GetCommunityChildDto } from '../dtos/get.community.dto'; import { GetCommunityChildDto } from '../dtos/get.community.dto';
import { UpdateCommunityNameDto } from '../dtos/update.community.dto'; import { UpdateCommunityNameDto } from '../dtos/update.community.dto';
import { CheckUserCommunityGuard } from 'src/guards/user.community.guard'; import { CheckUserCommunityGuard } from 'src/guards/user.community.guard';
import { AdminRoleGuard } from 'src/guards/admin.role.guard';
import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard';
import { CommunityPermissionGuard } from 'src/guards/community.permission.guard';
@ApiTags('Community Module') @ApiTags('Community Module')
@Controller({ @Controller({
@ -30,13 +32,18 @@ export class CommunityController {
constructor(private readonly communityService: CommunityService) {} constructor(private readonly communityService: CommunityService) {}
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(JwtAuthGuard) @UseGuards(AdminRoleGuard)
@Post() @Post()
async addCommunity(@Body() addCommunityDto: AddCommunityDto) { async addCommunity(@Body() addCommunityDto: AddCommunityDto) {
try { try {
const community = const community =
await this.communityService.addCommunity(addCommunityDto); await this.communityService.addCommunity(addCommunityDto);
return { message: 'Community added successfully', uuid: community.uuid }; return {
statusCode: HttpStatus.CREATED,
success: true,
message: 'Community added successfully',
data: community,
};
} catch (error) { } catch (error) {
throw new HttpException( throw new HttpException(
error.message || 'Internal server error', error.message || 'Internal server error',
@ -46,7 +53,7 @@ export class CommunityController {
} }
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard, CommunityPermissionGuard)
@Get(':communityUuid') @Get(':communityUuid')
async getCommunityByUuid(@Param('communityUuid') communityUuid: string) { async getCommunityByUuid(@Param('communityUuid') communityUuid: string) {
try { try {
@ -62,7 +69,7 @@ export class CommunityController {
} }
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard, CommunityPermissionGuard)
@Get('child/:communityUuid') @Get('child/:communityUuid')
async getCommunityChildByUuid( async getCommunityChildByUuid(
@Param('communityUuid') communityUuid: string, @Param('communityUuid') communityUuid: string,
@ -96,12 +103,16 @@ export class CommunityController {
} }
} }
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(JwtAuthGuard, CheckUserCommunityGuard) @UseGuards(AdminRoleGuard, CheckUserCommunityGuard)
@Post('user') @Post('user')
async addUserCommunity(@Body() addUserCommunityDto: AddUserCommunityDto) { async addUserCommunity(@Body() addUserCommunityDto: AddUserCommunityDto) {
try { try {
await this.communityService.addUserCommunity(addUserCommunityDto); await this.communityService.addUserCommunity(addUserCommunityDto);
return { message: 'user community added successfully' }; return {
statusCode: HttpStatus.CREATED,
success: true,
message: 'user community added successfully',
};
} catch (error) { } catch (error) {
throw new HttpException( throw new HttpException(
error.message || 'Internal server error', error.message || 'Internal server error',
@ -110,7 +121,7 @@ export class CommunityController {
} }
} }
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard, CommunityPermissionGuard)
@Put('rename/:communityUuid') @Put('rename/:communityUuid')
async renameCommunityByUuid( async renameCommunityByUuid(
@Param('communityUuid') communityUuid: string, @Param('communityUuid') communityUuid: string,

View File

@ -9,6 +9,7 @@ import {
HttpException, HttpException,
HttpStatus, HttpStatus,
UseGuards, UseGuards,
Req,
} from '@nestjs/common'; } from '@nestjs/common';
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
import { import {
@ -22,6 +23,8 @@ import {
import { ControlDeviceDto } from '../dtos/control.device.dto'; import { ControlDeviceDto } from '../dtos/control.device.dto';
import { CheckRoomGuard } from 'src/guards/room.guard'; import { CheckRoomGuard } from 'src/guards/room.guard';
import { CheckGroupGuard } from 'src/guards/group.guard'; import { CheckGroupGuard } from 'src/guards/group.guard';
import { CheckUserHavePermission } from 'src/guards/user.device.permission.guard';
import { CheckUserHaveControllablePermission } from 'src/guards/user.device.controllable.permission.guard';
import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard';
@ApiTags('Device Module') @ApiTags('Device Module')
@ -37,10 +40,13 @@ export class DeviceController {
@Get('room') @Get('room')
async getDevicesByRoomId( async getDevicesByRoomId(
@Query() getDeviceByRoomUuidDto: GetDeviceByRoomUuidDto, @Query() getDeviceByRoomUuidDto: GetDeviceByRoomUuidDto,
@Req() req: any,
) { ) {
try { try {
const userUuid = req.user.uuid;
return await this.deviceService.getDevicesByRoomId( return await this.deviceService.getDevicesByRoomId(
getDeviceByRoomUuidDto, getDeviceByRoomUuidDto,
userUuid,
); );
} catch (error) { } catch (error) {
throw new HttpException( throw new HttpException(
@ -55,7 +61,15 @@ export class DeviceController {
@Post('room') @Post('room')
async addDeviceInRoom(@Body() addDeviceInRoomDto: AddDeviceInRoomDto) { async addDeviceInRoom(@Body() addDeviceInRoomDto: AddDeviceInRoomDto) {
try { try {
return await this.deviceService.addDeviceInRoom(addDeviceInRoomDto); const device =
await this.deviceService.addDeviceInRoom(addDeviceInRoomDto);
return {
statusCode: HttpStatus.CREATED,
success: true,
message: 'device added in room successfully',
data: device,
};
} catch (error) { } catch (error) {
throw new HttpException( throw new HttpException(
error.message || 'Internal server error', error.message || 'Internal server error',
@ -68,10 +82,13 @@ export class DeviceController {
@Get('group') @Get('group')
async getDevicesByGroupId( async getDevicesByGroupId(
@Query() getDeviceByGroupIdDto: GetDeviceByGroupIdDto, @Query() getDeviceByGroupIdDto: GetDeviceByGroupIdDto,
@Req() req: any,
) { ) {
try { try {
const userUuid = req.user.uuid;
return await this.deviceService.getDevicesByGroupId( return await this.deviceService.getDevicesByGroupId(
getDeviceByGroupIdDto, getDeviceByGroupIdDto,
userUuid,
); );
} catch (error) { } catch (error) {
throw new HttpException( throw new HttpException(
@ -94,11 +111,18 @@ export class DeviceController {
} }
} }
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard, CheckUserHavePermission)
@Get(':deviceUuid') @Get(':deviceUuid')
async getDeviceDetailsByDeviceId(@Param('deviceUuid') deviceUuid: string) { async getDeviceDetailsByDeviceId(
@Param('deviceUuid') deviceUuid: string,
@Req() req: any,
) {
try { try {
return await this.deviceService.getDeviceDetailsByDeviceId(deviceUuid); const userUuid = req.user.uuid;
return await this.deviceService.getDeviceDetailsByDeviceId(
deviceUuid,
userUuid,
);
} catch (error) { } catch (error) {
throw new HttpException( throw new HttpException(
error.message || 'Internal server error', error.message || 'Internal server error',
@ -107,7 +131,7 @@ export class DeviceController {
} }
} }
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard, CheckUserHavePermission)
@Get(':deviceUuid/functions') @Get(':deviceUuid/functions')
async getDeviceInstructionByDeviceId( async getDeviceInstructionByDeviceId(
@Param('deviceUuid') deviceUuid: string, @Param('deviceUuid') deviceUuid: string,
@ -124,7 +148,7 @@ export class DeviceController {
} }
} }
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard, CheckUserHavePermission)
@Get(':deviceUuid/functions/status') @Get(':deviceUuid/functions/status')
async getDevicesInstructionStatus(@Param('deviceUuid') deviceUuid: string) { async getDevicesInstructionStatus(@Param('deviceUuid') deviceUuid: string) {
try { try {
@ -138,11 +162,17 @@ export class DeviceController {
} }
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard, CheckUserHaveControllablePermission)
@Post('control') @Post(':deviceUuid/control')
async controlDevice(@Body() controlDeviceDto: ControlDeviceDto) { async controlDevice(
@Body() controlDeviceDto: ControlDeviceDto,
@Param('deviceUuid') deviceUuid: string,
) {
try { try {
return await this.deviceService.controlDevice(controlDeviceDto); return await this.deviceService.controlDevice(
controlDeviceDto,
deviceUuid,
);
} catch (error) { } catch (error) {
throw new HttpException( throw new HttpException(
error.message || 'Internal server error', error.message || 'Internal server error',

View File

@ -5,15 +5,14 @@ import { ConfigModule } from '@nestjs/config';
import { ProductRepositoryModule } from '@app/common/modules/product/product.repository.module'; import { ProductRepositoryModule } from '@app/common/modules/product/product.repository.module';
import { ProductRepository } from '@app/common/modules/product/repositories'; import { ProductRepository } from '@app/common/modules/product/repositories';
import { DeviceRepositoryModule } from '@app/common/modules/device'; import { DeviceRepositoryModule } from '@app/common/modules/device';
import { import { DeviceRepository } from '@app/common/modules/device/repositories';
DeviceRepository,
DeviceUserTypeRepository,
} from '@app/common/modules/device/repositories';
import { PermissionTypeRepository } from '@app/common/modules/permission/repositories'; import { PermissionTypeRepository } from '@app/common/modules/permission/repositories';
import { SpaceRepository } from '@app/common/modules/space/repositories'; import { SpaceRepository } from '@app/common/modules/space/repositories';
import { GroupDeviceRepository } from '@app/common/modules/group-device/repositories'; import { GroupDeviceRepository } from '@app/common/modules/group-device/repositories';
import { GroupRepository } from '@app/common/modules/group/repositories'; import { GroupRepository } from '@app/common/modules/group/repositories';
import { GroupRepositoryModule } from '@app/common/modules/group/group.repository.module'; 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({ @Module({
imports: [ imports: [
ConfigModule, ConfigModule,
@ -25,12 +24,13 @@ import { GroupRepositoryModule } from '@app/common/modules/group/group.repositor
providers: [ providers: [
DeviceService, DeviceService,
ProductRepository, ProductRepository,
DeviceUserTypeRepository, DeviceUserPermissionRepository,
PermissionTypeRepository, PermissionTypeRepository,
SpaceRepository, SpaceRepository,
DeviceRepository, DeviceRepository,
GroupDeviceRepository, GroupDeviceRepository,
GroupRepository, GroupRepository,
UserRepository,
], ],
exports: [DeviceService], exports: [DeviceService],
}) })

View File

@ -2,14 +2,6 @@ import { ApiProperty } from '@nestjs/swagger';
import { IsNotEmpty, IsString } from 'class-validator'; import { IsNotEmpty, IsString } from 'class-validator';
export class ControlDeviceDto { export class ControlDeviceDto {
@ApiProperty({
description: 'deviceUuid',
required: true,
})
@IsString()
@IsNotEmpty()
public deviceUuid: string;
@ApiProperty({ @ApiProperty({
description: 'code', description: 'code',
required: true, required: true,

View File

@ -4,7 +4,6 @@ import {
HttpException, HttpException,
HttpStatus, HttpStatus,
NotFoundException, NotFoundException,
BadRequestException,
} from '@nestjs/common'; } from '@nestjs/common';
import { TuyaContext } from '@tuya/tuya-connector-nodejs'; import { TuyaContext } from '@tuya/tuya-connector-nodejs';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
@ -18,7 +17,6 @@ import {
GetDeviceDetailsFunctionsStatusInterface, GetDeviceDetailsFunctionsStatusInterface,
GetDeviceDetailsInterface, GetDeviceDetailsInterface,
controlDeviceInterface, controlDeviceInterface,
updateDeviceFirmwareInterface,
} from '../interfaces/get.device.interface'; } from '../interfaces/get.device.interface';
import { import {
GetDeviceByGroupIdDto, GetDeviceByGroupIdDto,
@ -29,6 +27,8 @@ import { convertKeysToCamelCase } from '@app/common/helper/camelCaseConverter';
import { DeviceRepository } from '@app/common/modules/device/repositories'; import { DeviceRepository } from '@app/common/modules/device/repositories';
import { GroupDeviceRepository } from '@app/common/modules/group-device/repositories'; import { GroupDeviceRepository } from '@app/common/modules/group-device/repositories';
import { ProductType } from '@app/common/constants/product-type.enum'; import { ProductType } from '@app/common/constants/product-type.enum';
import { PermissionType } from '@app/common/constants/permission-type.enum';
import { In } from 'typeorm';
@Injectable() @Injectable()
export class DeviceService { export class DeviceService {
@ -50,13 +50,25 @@ export class DeviceService {
async getDevicesByRoomId( async getDevicesByRoomId(
getDeviceByRoomUuidDto: GetDeviceByRoomUuidDto, getDeviceByRoomUuidDto: GetDeviceByRoomUuidDto,
userUuid: string,
): Promise<GetDeviceDetailsInterface[]> { ): Promise<GetDeviceDetailsInterface[]> {
try { try {
const devices = await this.deviceRepository.find({ const devices = await this.deviceRepository.find({
where: { where: {
spaceDevice: { uuid: getDeviceByRoomUuidDto.roomUuid }, spaceDevice: { uuid: getDeviceByRoomUuidDto.roomUuid },
permission: {
userUuid,
permissionType: {
type: In([PermissionType.READ, PermissionType.CONTROLLABLE]),
},
},
}, },
relations: ['spaceDevice', 'productDevice'], relations: [
'spaceDevice',
'productDevice',
'permission',
'permission.permissionType',
],
}); });
const devicesData = await Promise.all( const devicesData = await Promise.all(
devices.map(async (device) => { devices.map(async (device) => {
@ -67,9 +79,11 @@ export class DeviceService {
uuid: device.uuid, uuid: device.uuid,
productUuid: device.productDevice.uuid, productUuid: device.productDevice.uuid,
productType: device.productDevice.prodType, productType: device.productDevice.prodType,
permissionType: device.permission[0].permissionType.type,
} as GetDeviceDetailsInterface; } as GetDeviceDetailsInterface;
}), }),
); );
return devicesData; return devicesData;
} catch (error) { } catch (error) {
// Handle the error here // Handle the error here
@ -80,11 +94,29 @@ export class DeviceService {
} }
} }
async getDevicesByGroupId(getDeviceByGroupIdDto: GetDeviceByGroupIdDto) { async getDevicesByGroupId(
getDeviceByGroupIdDto: GetDeviceByGroupIdDto,
userUuid: string,
) {
try { try {
const groupDevices = await this.groupDeviceRepository.find({ const groupDevices = await this.groupDeviceRepository.find({
where: { group: { uuid: getDeviceByGroupIdDto.groupUuid } }, where: {
relations: ['device'], 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( const devicesData = await Promise.all(
groupDevices.map(async (device) => { groupDevices.map(async (device) => {
@ -92,9 +124,10 @@ export class DeviceService {
...(await this.getDeviceDetailsByDeviceIdTuya( ...(await this.getDeviceDetailsByDeviceIdTuya(
device.device.deviceTuyaUuid, device.device.deviceTuyaUuid,
)), )),
uuid: device.uuid, uuid: device.device.uuid,
productUuid: device.device.productDevice.uuid, productUuid: device.device.productDevice.uuid,
productType: device.device.productDevice.prodType, productType: device.device.productDevice.prodType,
permissionType: device.device.permission[0].permissionType.type,
} as GetDeviceDetailsInterface; } as GetDeviceDetailsInterface;
}), }),
); );
@ -107,7 +140,6 @@ export class DeviceService {
); );
} }
} }
async addDeviceInRoom(addDeviceInRoomDto: AddDeviceInRoomDto) { async addDeviceInRoom(addDeviceInRoomDto: AddDeviceInRoomDto) {
try { try {
const device = await this.getDeviceDetailsByDeviceIdTuya( const device = await this.getDeviceDetailsByDeviceIdTuya(
@ -118,12 +150,11 @@ export class DeviceService {
throw new Error('Product UUID is missing for the device.'); throw new Error('Product UUID is missing for the device.');
} }
await this.deviceRepository.save({ return await this.deviceRepository.save({
deviceTuyaUuid: addDeviceInRoomDto.deviceTuyaUuid, deviceTuyaUuid: addDeviceInRoomDto.deviceTuyaUuid,
spaceDevice: { uuid: addDeviceInRoomDto.roomUuid }, spaceDevice: { uuid: addDeviceInRoomDto.roomUuid },
productDevice: { uuid: device.productUuid }, productDevice: { uuid: device.productUuid },
}); });
return { message: 'device added in room successfully' };
} catch (error) { } catch (error) {
if (error.code === '23505') { if (error.code === '23505') {
throw new HttpException( throw new HttpException(
@ -161,12 +192,9 @@ export class DeviceService {
} }
} }
async controlDevice(controlDeviceDto: ControlDeviceDto) { async controlDevice(controlDeviceDto: ControlDeviceDto, deviceUuid: string) {
try { try {
const deviceDetails = await this.getDeviceByDeviceUuid( const deviceDetails = await this.getDeviceByDeviceUuid(deviceUuid, false);
controlDeviceDto.deviceUuid,
false,
);
if (!deviceDetails || !deviceDetails.deviceTuyaUuid) { if (!deviceDetails || !deviceDetails.deviceTuyaUuid) {
throw new NotFoundException('Device Not Found'); throw new NotFoundException('Device Not Found');
@ -185,7 +213,10 @@ export class DeviceService {
); );
} }
} catch (error) { } 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( async controlDeviceTuya(
@ -213,8 +244,19 @@ export class DeviceService {
} }
} }
async getDeviceDetailsByDeviceId(deviceUuid: string) { async getDeviceDetailsByDeviceId(deviceUuid: string, userUuid: string) {
try { try {
const userDevicePermission = await this.getUserDevicePermission(
userUuid,
deviceUuid,
);
const deviceDetails = await this.deviceRepository.findOne({
where: {
uuid: deviceUuid,
},
relations: ['productDevice'],
});
const deviceDetails = await this.getDeviceByDeviceUuid(deviceUuid); const deviceDetails = await this.getDeviceByDeviceUuid(deviceUuid);
if (!deviceDetails) { if (!deviceDetails) {
@ -230,9 +272,13 @@ export class DeviceService {
uuid: deviceDetails.uuid, uuid: deviceDetails.uuid,
productUuid: deviceDetails.productDevice.uuid, productUuid: deviceDetails.productDevice.uuid,
productType: deviceDetails.productDevice.prodType, productType: deviceDetails.productDevice.prodType,
permissionType: userDevicePermission,
}; };
} catch (error) { } catch (error) {
throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); throw new HttpException(
error.message || 'Device Not Found',
HttpStatus.NOT_FOUND,
);
} }
} }
async getDeviceDetailsByDeviceIdTuya( async getDeviceDetailsByDeviceIdTuya(
@ -255,6 +301,7 @@ export class DeviceService {
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
const { productName, productId, id, ...rest } = camelCaseResponse.result; const { productName, productId, id, ...rest } = camelCaseResponse.result;
return { return {
...rest, ...rest,
productUuid: product.uuid, productUuid: product.uuid,
@ -465,4 +512,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;
}
} }

View File

@ -12,12 +12,14 @@ import {
UseGuards, UseGuards,
} from '@nestjs/common'; } from '@nestjs/common';
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
import { JwtAuthGuard } from '../../../libs/common/src/guards/jwt.auth.guard';
import { AddFloorDto, AddUserFloorDto } from '../dtos/add.floor.dto'; import { AddFloorDto, AddUserFloorDto } from '../dtos/add.floor.dto';
import { GetFloorChildDto } from '../dtos/get.floor.dto'; import { GetFloorChildDto } from '../dtos/get.floor.dto';
import { UpdateFloorNameDto } from '../dtos/update.floor.dto'; import { UpdateFloorNameDto } from '../dtos/update.floor.dto';
import { CheckBuildingTypeGuard } from 'src/guards/building.type.guard'; import { CheckBuildingTypeGuard } from 'src/guards/building.type.guard';
import { CheckUserFloorGuard } from 'src/guards/user.floor.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';
@ApiTags('Floor Module') @ApiTags('Floor Module')
@Controller({ @Controller({
@ -28,12 +30,17 @@ export class FloorController {
constructor(private readonly floorService: FloorService) {} constructor(private readonly floorService: FloorService) {}
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(JwtAuthGuard, CheckBuildingTypeGuard) @UseGuards(AdminRoleGuard, CheckBuildingTypeGuard)
@Post() @Post()
async addFloor(@Body() addFloorDto: AddFloorDto) { async addFloor(@Body() addFloorDto: AddFloorDto) {
try { try {
const floor = await this.floorService.addFloor(addFloorDto); const floor = await this.floorService.addFloor(addFloorDto);
return { message: 'Floor added successfully', uuid: floor.uuid }; return {
statusCode: HttpStatus.CREATED,
success: true,
message: 'Floor added successfully',
data: floor,
};
} catch (error) { } catch (error) {
throw new HttpException( throw new HttpException(
error.message || 'Internal server error', error.message || 'Internal server error',
@ -43,7 +50,7 @@ export class FloorController {
} }
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard, FloorPermissionGuard)
@Get(':floorUuid') @Get(':floorUuid')
async getFloorByUuid(@Param('floorUuid') floorUuid: string) { async getFloorByUuid(@Param('floorUuid') floorUuid: string) {
try { try {
@ -58,7 +65,7 @@ export class FloorController {
} }
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard, FloorPermissionGuard)
@Get('child/:floorUuid') @Get('child/:floorUuid')
async getFloorChildByUuid( async getFloorChildByUuid(
@Param('floorUuid') floorUuid: string, @Param('floorUuid') floorUuid: string,
@ -78,7 +85,7 @@ export class FloorController {
} }
} }
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard, FloorPermissionGuard)
@Get('parent/:floorUuid') @Get('parent/:floorUuid')
async getFloorParentByUuid(@Param('floorUuid') floorUuid: string) { async getFloorParentByUuid(@Param('floorUuid') floorUuid: string) {
try { try {
@ -93,12 +100,16 @@ export class FloorController {
} }
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(JwtAuthGuard, CheckUserFloorGuard) @UseGuards(AdminRoleGuard, CheckUserFloorGuard)
@Post('user') @Post('user')
async addUserFloor(@Body() addUserFloorDto: AddUserFloorDto) { async addUserFloor(@Body() addUserFloorDto: AddUserFloorDto) {
try { try {
await this.floorService.addUserFloor(addUserFloorDto); await this.floorService.addUserFloor(addUserFloorDto);
return { message: 'user floor added successfully' }; return {
statusCode: HttpStatus.CREATED,
success: true,
message: 'user floor added successfully',
};
} catch (error) { } catch (error) {
throw new HttpException( throw new HttpException(
error.message || 'Internal server error', error.message || 'Internal server error',
@ -122,7 +133,7 @@ export class FloorController {
} }
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard, FloorPermissionGuard)
@Put('rename/:floorUuid') @Put('rename/:floorUuid')
async renameFloorByUuid( async renameFloorByUuid(
@Param('floorUuid') floorUuid: string, @Param('floorUuid') floorUuid: string,

View File

@ -12,11 +12,11 @@ import {
HttpStatus, HttpStatus,
} from '@nestjs/common'; } from '@nestjs/common';
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
import { JwtAuthGuard } from '../../../libs/common/src/guards/jwt.auth.guard';
import { AddGroupDto } from '../dtos/add.group.dto'; import { AddGroupDto } from '../dtos/add.group.dto';
import { ControlGroupDto } from '../dtos/control.group.dto'; import { ControlGroupDto } from '../dtos/control.group.dto';
import { RenameGroupDto } from '../dtos/rename.group.dto copy'; import { RenameGroupDto } from '../dtos/rename.group.dto copy';
import { CheckProductUuidForAllDevicesGuard } from 'src/guards/device.product.guard'; import { CheckProductUuidForAllDevicesGuard } from 'src/guards/device.product.guard';
import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard';
@ApiTags('Group Module') @ApiTags('Group Module')
@Controller({ @Controller({

View File

@ -0,0 +1,20 @@
import { RoleType } from '@app/common/constants/role.type.enum';
import { BadRequestException, UnauthorizedException } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
export class AdminRoleGuard extends AuthGuard('jwt') {
handleRequest(err, user) {
if (err || !user) {
throw err || new UnauthorizedException();
} else {
const isAdmin = user.roles.some(
(role) =>
role.type === RoleType.SUPER_ADMIN || role.type === RoleType.ADMIN,
);
if (!isAdmin) {
throw new BadRequestException('Only admin role can access this route');
}
}
return user;
}
}

View File

@ -0,0 +1,35 @@
import { SpacePermissionService } from '@app/common/helper/services/space.permission.service';
import {
BadRequestException,
CanActivate,
ExecutionContext,
Injectable,
} from '@nestjs/common';
@Injectable()
export class BuildingPermissionGuard implements CanActivate {
constructor(private readonly permissionService: SpacePermissionService) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const req = context.switchToHttp().getRequest();
try {
const { buildingUuid } = req.params;
const { user } = req;
if (!buildingUuid) {
throw new BadRequestException('buildingUuid is required');
}
await this.permissionService.checkUserPermission(
buildingUuid,
user.uuid,
'building',
);
return true;
} catch (error) {
throw error;
}
}
}

View File

@ -0,0 +1,35 @@
import { SpacePermissionService } from '@app/common/helper/services/space.permission.service';
import {
BadRequestException,
CanActivate,
ExecutionContext,
Injectable,
} from '@nestjs/common';
@Injectable()
export class CommunityPermissionGuard implements CanActivate {
constructor(private readonly permissionService: SpacePermissionService) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const req = context.switchToHttp().getRequest();
try {
const { communityUuid } = req.params;
const { user } = req;
if (!communityUuid) {
throw new BadRequestException('communityUuid is required');
}
await this.permissionService.checkUserPermission(
communityUuid,
user.uuid,
'community',
);
return true;
} catch (error) {
throw error;
}
}
}

View File

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

View File

@ -0,0 +1,35 @@
import { SpacePermissionService } from '@app/common/helper/services/space.permission.service';
import {
BadRequestException,
CanActivate,
ExecutionContext,
Injectable,
} from '@nestjs/common';
@Injectable()
export class FloorPermissionGuard implements CanActivate {
constructor(private readonly permissionService: SpacePermissionService) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const req = context.switchToHttp().getRequest();
try {
const { floorUuid } = req.params;
const { user } = req;
if (!floorUuid) {
throw new BadRequestException('floorUuid is required');
}
await this.permissionService.checkUserPermission(
floorUuid,
user.uuid,
'floor',
);
return true;
} catch (error) {
throw error;
}
}
}

View File

@ -0,0 +1,35 @@
import { SpacePermissionService } from '@app/common/helper/services/space.permission.service';
import {
BadRequestException,
CanActivate,
ExecutionContext,
Injectable,
} from '@nestjs/common';
@Injectable()
export class RoomPermissionGuard implements CanActivate {
constructor(private readonly permissionService: SpacePermissionService) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const req = context.switchToHttp().getRequest();
try {
const { roomUuid } = req.params;
const { user } = req;
if (!roomUuid) {
throw new BadRequestException('roomUuid is required');
}
await this.permissionService.checkUserPermission(
roomUuid,
user.uuid,
'room',
);
return true;
} catch (error) {
throw error;
}
}
}

View File

@ -0,0 +1,21 @@
import { RoleType } from '@app/common/constants/role.type.enum';
import { BadRequestException, UnauthorizedException } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
export class SuperAdminRoleGuard extends AuthGuard('jwt') {
handleRequest(err, user) {
if (err || !user) {
throw err || new UnauthorizedException();
} else {
const isSuperAdmin = user.roles.some(
(role) => role.type === RoleType.SUPER_ADMIN,
);
if (!isSuperAdmin) {
throw new BadRequestException(
'Only super admin role can access this route',
);
}
}
return user;
}
}

View File

@ -0,0 +1,35 @@
import { SpacePermissionService } from '@app/common/helper/services/space.permission.service';
import {
BadRequestException,
CanActivate,
ExecutionContext,
Injectable,
} from '@nestjs/common';
@Injectable()
export class UnitPermissionGuard implements CanActivate {
constructor(private readonly permissionService: SpacePermissionService) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const req = context.switchToHttp().getRequest();
try {
const { unitUuid } = req.params;
const { user } = req;
if (!unitUuid) {
throw new BadRequestException('unitUuid is required');
}
await this.permissionService.checkUserPermission(
unitUuid,
user.uuid,
'unit',
);
return true;
} catch (error) {
throw error;
}
}
}

View 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,
});
}
}
}

View 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,
});
}
}
}

View File

@ -4,6 +4,7 @@ import rateLimit from 'express-rate-limit';
import helmet from 'helmet'; import helmet from 'helmet';
import { setupSwaggerAuthentication } from '../libs/common/src/util/user-auth.swagger.utils'; import { setupSwaggerAuthentication } from '../libs/common/src/util/user-auth.swagger.utils';
import { ValidationPipe } from '@nestjs/common'; import { ValidationPipe } from '@nestjs/common';
import { SeederService } from '@app/common/seed/services/seeder.service';
async function bootstrap() { async function bootstrap() {
const app = await NestFactory.create(AuthModule); const app = await NestFactory.create(AuthModule);
@ -34,6 +35,13 @@ async function bootstrap() {
}), }),
); );
const seederService = app.get(SeederService);
try {
await seederService.seed();
console.log('Seeding complete!');
} catch (error) {
console.error('Seeding failed!', error);
}
await app.listen(process.env.PORT || 4000); await app.listen(process.env.PORT || 4000);
} }
console.log('Starting auth at port ...', process.env.PORT || 4000); console.log('Starting auth at port ...', process.env.PORT || 4000);

View File

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

View File

@ -0,0 +1,54 @@
import {
Body,
Controller,
Get,
HttpException,
HttpStatus,
Post,
UseGuards,
} from '@nestjs/common';
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
import { RoleService } from '../services/role.service';
import { AddUserRoleDto } from '../dtos';
import { SuperAdminRoleGuard } from 'src/guards/super.admin.role.guard';
@ApiTags('Role Module')
@Controller({
version: '1',
path: 'role',
})
export class RoleController {
constructor(private readonly roleService: RoleService) {}
@ApiBearerAuth()
@UseGuards(SuperAdminRoleGuard)
@Get('types')
async fetchRoleTypes() {
try {
const roleTypes = await this.roleService.fetchRoleTypes();
return {
statusCode: HttpStatus.OK,
message: 'Role Types fetched Successfully',
data: roleTypes,
};
} catch (err) {
throw new Error(err);
}
}
@ApiBearerAuth()
@UseGuards(SuperAdminRoleGuard)
@Post()
async addUserRoleType(@Body() addUserRoleDto: AddUserRoleDto) {
try {
await this.roleService.addUserRoleType(addUserRoleDto);
return {
statusCode: HttpStatus.OK,
message: 'User Role Added Successfully',
};
} catch (error) {
throw new HttpException(
error.message || 'Internal server error',
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
}

1
src/role/dtos/index.ts Normal file
View File

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

View File

@ -0,0 +1,24 @@
import { RoleType } from '@app/common/constants/role.type.enum';
import { ApiProperty } from '@nestjs/swagger';
import { IsEnum, IsIn, IsNotEmpty, IsString } from 'class-validator';
export class AddUserRoleDto {
@ApiProperty({
description: 'userUuid',
required: true,
})
@IsString()
@IsNotEmpty()
public userUuid: string;
@ApiProperty({
description: 'Role type (ADMIN)',
enum: [RoleType.ADMIN],
required: true,
})
@IsEnum(RoleType)
@IsIn([RoleType.ADMIN], {
message: 'roleType must be one of the following values: ADMIN',
})
roleType: RoleType;
}

25
src/role/role.module.ts Normal file
View File

@ -0,0 +1,25 @@
import { DeviceRepositoryModule } from '@app/common/modules/device';
import { DeviceRepository } from '@app/common/modules/device/repositories';
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { RoleService } from './services/role.service';
import { RoleController } from './controllers/role.controller';
import { DeviceUserPermissionRepository } from '@app/common/modules/device-user-permission/repositories';
import { PermissionTypeRepository } from '@app/common/modules/permission/repositories';
import { RoleTypeRepository } from '@app/common/modules/role-type/repositories';
import { UserRoleRepository } from '@app/common/modules/user-role/repositories';
@Module({
imports: [ConfigModule, DeviceRepositoryModule],
controllers: [RoleController],
providers: [
DeviceUserPermissionRepository,
PermissionTypeRepository,
DeviceRepository,
RoleService,
RoleTypeRepository,
UserRoleRepository,
],
exports: [RoleService],
})
export class RoleModule {}

View File

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

View File

@ -0,0 +1,54 @@
import { RoleTypeRepository } from './../../../libs/common/src/modules/role-type/repositories/role.type.repository';
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { AddUserRoleDto } from '../dtos/role.add.dto';
import { UserRoleRepository } from '@app/common/modules/user-role/repositories';
import { QueryFailedError } from 'typeorm';
@Injectable()
export class RoleService {
constructor(
private readonly roleTypeRepository: RoleTypeRepository,
private readonly userRoleRepository: UserRoleRepository,
) {}
async addUserRoleType(addUserRoleDto: AddUserRoleDto) {
try {
const roleType = await this.fetchRoleByType(addUserRoleDto.roleType);
if (roleType.uuid) {
return await this.userRoleRepository.save({
user: { uuid: addUserRoleDto.userUuid },
roleType: { uuid: roleType.uuid },
});
}
} catch (error) {
if (
error instanceof QueryFailedError &&
error.driverError.code === '23505'
) {
// Postgres unique constraint violation error code
throw new HttpException(
'This role already exists for this user',
HttpStatus.CONFLICT,
);
}
throw new HttpException(
error.message || 'Internal Server Error',
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
async fetchRoleTypes() {
const roleTypes = await this.roleTypeRepository.find();
return roleTypes;
}
private async fetchRoleByType(roleType: string) {
return await this.roleTypeRepository.findOne({
where: {
type: roleType,
},
});
}
}

View File

@ -11,11 +11,13 @@ import {
UseGuards, UseGuards,
} from '@nestjs/common'; } from '@nestjs/common';
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
import { JwtAuthGuard } from '../../../libs/common/src/guards/jwt.auth.guard';
import { AddRoomDto, AddUserRoomDto } from '../dtos/add.room.dto'; import { AddRoomDto, AddUserRoomDto } from '../dtos/add.room.dto';
import { UpdateRoomNameDto } from '../dtos/update.room.dto'; import { UpdateRoomNameDto } from '../dtos/update.room.dto';
import { CheckUnitTypeGuard } from 'src/guards/unit.type.guard'; import { CheckUnitTypeGuard } from 'src/guards/unit.type.guard';
import { CheckUserRoomGuard } from 'src/guards/user.room.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';
@ApiTags('Room Module') @ApiTags('Room Module')
@Controller({ @Controller({
@ -26,12 +28,17 @@ export class RoomController {
constructor(private readonly roomService: RoomService) {} constructor(private readonly roomService: RoomService) {}
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(JwtAuthGuard, CheckUnitTypeGuard) @UseGuards(AdminRoleGuard, CheckUnitTypeGuard)
@Post() @Post()
async addRoom(@Body() addRoomDto: AddRoomDto) { async addRoom(@Body() addRoomDto: AddRoomDto) {
try { try {
const room = await this.roomService.addRoom(addRoomDto); const room = await this.roomService.addRoom(addRoomDto);
return { message: 'Room added successfully', uuid: room.uuid }; return {
statusCode: HttpStatus.CREATED,
success: true,
message: 'Room added successfully',
data: room,
};
} catch (error) { } catch (error) {
throw new HttpException( throw new HttpException(
error.message || 'Internal server error', error.message || 'Internal server error',
@ -41,7 +48,7 @@ export class RoomController {
} }
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard, RoomPermissionGuard)
@Get(':roomUuid') @Get(':roomUuid')
async getRoomByUuid(@Param('roomUuid') roomUuid: string) { async getRoomByUuid(@Param('roomUuid') roomUuid: string) {
try { try {
@ -56,7 +63,7 @@ export class RoomController {
} }
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard, RoomPermissionGuard)
@Get('parent/:roomUuid') @Get('parent/:roomUuid')
async getRoomParentByUuid(@Param('roomUuid') roomUuid: string) { async getRoomParentByUuid(@Param('roomUuid') roomUuid: string) {
try { try {
@ -70,12 +77,16 @@ export class RoomController {
} }
} }
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(JwtAuthGuard, CheckUserRoomGuard) @UseGuards(AdminRoleGuard, CheckUserRoomGuard)
@Post('user') @Post('user')
async addUserRoom(@Body() addUserRoomDto: AddUserRoomDto) { async addUserRoom(@Body() addUserRoomDto: AddUserRoomDto) {
try { try {
await this.roomService.addUserRoom(addUserRoomDto); await this.roomService.addUserRoom(addUserRoomDto);
return { message: 'user room added successfully' }; return {
statusCode: HttpStatus.CREATED,
success: true,
message: 'user room added successfully',
};
} catch (error) { } catch (error) {
throw new HttpException( throw new HttpException(
error.message || 'Internal server error', error.message || 'Internal server error',
@ -98,7 +109,7 @@ export class RoomController {
} }
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard, RoomPermissionGuard)
@Put('rename/:roomUuid') @Put('rename/:roomUuid')
async renameRoomByUuid( async renameRoomByUuid(
@Param('roomUuid') roomUuid: string, @Param('roomUuid') roomUuid: string,

View File

@ -12,12 +12,14 @@ import {
UseGuards, UseGuards,
} from '@nestjs/common'; } from '@nestjs/common';
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
import { JwtAuthGuard } from '../../../libs/common/src/guards/jwt.auth.guard';
import { AddUnitDto, AddUserUnitDto } from '../dtos/add.unit.dto'; import { AddUnitDto, AddUserUnitDto } from '../dtos/add.unit.dto';
import { GetUnitChildDto } from '../dtos/get.unit.dto'; import { GetUnitChildDto } from '../dtos/get.unit.dto';
import { UpdateUnitNameDto } from '../dtos/update.unit.dto'; import { UpdateUnitNameDto } from '../dtos/update.unit.dto';
import { CheckFloorTypeGuard } from 'src/guards/floor.type.guard'; import { CheckFloorTypeGuard } from 'src/guards/floor.type.guard';
import { CheckUserUnitGuard } from 'src/guards/user.unit.guard'; import { CheckUserUnitGuard } from 'src/guards/user.unit.guard';
import { AdminRoleGuard } from 'src/guards/admin.role.guard';
import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard';
import { UnitPermissionGuard } from 'src/guards/unit.permission.guard';
@ApiTags('Unit Module') @ApiTags('Unit Module')
@Controller({ @Controller({
@ -28,12 +30,17 @@ export class UnitController {
constructor(private readonly unitService: UnitService) {} constructor(private readonly unitService: UnitService) {}
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(JwtAuthGuard, CheckFloorTypeGuard) @UseGuards(AdminRoleGuard, CheckFloorTypeGuard)
@Post() @Post()
async addUnit(@Body() addUnitDto: AddUnitDto) { async addUnit(@Body() addUnitDto: AddUnitDto) {
try { try {
const unit = await this.unitService.addUnit(addUnitDto); const unit = await this.unitService.addUnit(addUnitDto);
return { message: 'Unit added successfully', uuid: unit.uuid }; return {
statusCode: HttpStatus.CREATED,
success: true,
message: 'Unit added successfully',
data: unit,
};
} catch (error) { } catch (error) {
throw new HttpException( throw new HttpException(
error.message || 'Internal server error', error.message || 'Internal server error',
@ -43,7 +50,7 @@ export class UnitController {
} }
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard, UnitPermissionGuard)
@Get(':unitUuid') @Get(':unitUuid')
async getUnitByUuid(@Param('unitUuid') unitUuid: string) { async getUnitByUuid(@Param('unitUuid') unitUuid: string) {
try { try {
@ -58,7 +65,7 @@ export class UnitController {
} }
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard, UnitPermissionGuard)
@Get('child/:unitUuid') @Get('child/:unitUuid')
async getUnitChildByUuid( async getUnitChildByUuid(
@Param('unitUuid') unitUuid: string, @Param('unitUuid') unitUuid: string,
@ -75,7 +82,7 @@ export class UnitController {
} }
} }
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard, UnitPermissionGuard)
@Get('parent/:unitUuid') @Get('parent/:unitUuid')
async getUnitParentByUuid(@Param('unitUuid') unitUuid: string) { async getUnitParentByUuid(@Param('unitUuid') unitUuid: string) {
try { try {
@ -89,12 +96,16 @@ export class UnitController {
} }
} }
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(JwtAuthGuard, CheckUserUnitGuard) @UseGuards(AdminRoleGuard, CheckUserUnitGuard)
@Post('user') @Post('user')
async addUserUnit(@Body() addUserUnitDto: AddUserUnitDto) { async addUserUnit(@Body() addUserUnitDto: AddUserUnitDto) {
try { try {
await this.unitService.addUserUnit(addUserUnitDto); await this.unitService.addUserUnit(addUserUnitDto);
return { message: 'user unit added successfully' }; return {
statusCode: HttpStatus.CREATED,
success: true,
message: 'user unit added successfully',
};
} catch (error) { } catch (error) {
throw new HttpException( throw new HttpException(
error.message || 'Internal server error', error.message || 'Internal server error',
@ -117,7 +128,7 @@ export class UnitController {
} }
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard, UnitPermissionGuard)
@Put('rename/:unitUuid') @Put('rename/:unitUuid')
async renameUnitByUuid( async renameUnitByUuid(
@Param('unitUuid') unitUuid: string, @Param('unitUuid') unitUuid: string,

View File

@ -0,0 +1 @@
export * from './user-device-permission.controller';

View File

@ -1,7 +1,9 @@
import { import {
Body, Body,
Controller, Controller,
Delete,
Get, Get,
HttpException,
HttpStatus, HttpStatus,
Param, Param,
Post, Post,
@ -11,8 +13,8 @@ import {
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
import { UserDevicePermissionService } from '../services/user-device-permission.service'; import { UserDevicePermissionService } from '../services/user-device-permission.service';
import { UserDevicePermissionAddDto } from '../dtos/user-device-permission.add.dto'; import { UserDevicePermissionAddDto } from '../dtos/user-device-permission.add.dto';
import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard';
import { UserDevicePermissionEditDto } from '../dtos/user-device-permission.edit.dto'; import { UserDevicePermissionEditDto } from '../dtos/user-device-permission.edit.dto';
import { AdminRoleGuard } from 'src/guards/admin.role.guard';
@ApiTags('Device Permission Module') @ApiTags('Device Permission Module')
@Controller({ @Controller({
@ -25,7 +27,7 @@ export class UserDevicePermissionController {
) {} ) {}
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(JwtAuthGuard) @UseGuards(AdminRoleGuard)
@Post('add') @Post('add')
async addDevicePermission( async addDevicePermission(
@Body() userDevicePermissionDto: UserDevicePermissionAddDto, @Body() userDevicePermissionDto: UserDevicePermissionAddDto,
@ -40,40 +42,45 @@ export class UserDevicePermissionController {
message: 'User Permission for Devices Added Successfully', message: 'User Permission for Devices Added Successfully',
data: addDetails, data: addDetails,
}; };
} catch (err) { } catch (error) {
throw new Error(err); throw new HttpException(
error.message || 'Internal server error',
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
);
} }
} }
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(JwtAuthGuard) @UseGuards(AdminRoleGuard)
@Put('edit/:userId') @Put('edit/:devicePermissionUuid')
async editDevicePermission( async editDevicePermission(
@Param('userId') userId: string, @Param('devicePermissionUuid') devicePermissionUuid: string,
@Body() userDevicePermissionEditDto: UserDevicePermissionEditDto, @Body() userDevicePermissionEditDto: UserDevicePermissionEditDto,
) { ) {
try { try {
await this.userDevicePermissionService.editUserPermission( await this.userDevicePermissionService.editUserPermission(
userId, devicePermissionUuid,
userDevicePermissionEditDto, userDevicePermissionEditDto,
); );
return { return {
statusCode: HttpStatus.OK, statusCode: HttpStatus.OK,
message: 'User Permission for Devices Updated Successfully', message: 'User Permission for Devices Updated Successfully',
data: {},
}; };
} catch (err) { } catch (error) {
throw new Error(err); throw new HttpException(
error.message || 'Internal server error',
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
);
} }
} }
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(JwtAuthGuard) @UseGuards(AdminRoleGuard)
@Get('list') @Get(':deviceUuid/list')
async fetchDevicePermission() { async fetchDevicePermission(@Param('deviceUuid') deviceUuid: string) {
try { try {
const deviceDetails = const deviceDetails =
await this.userDevicePermissionService.fetchuserPermission(); await this.userDevicePermissionService.fetchUserPermission(deviceUuid);
return { return {
statusCode: HttpStatus.OK, statusCode: HttpStatus.OK,
message: 'Device Details fetched Successfully', message: 'Device Details fetched Successfully',
@ -83,4 +90,25 @@ export class UserDevicePermissionController {
throw new Error(err); throw new Error(err);
} }
} }
@ApiBearerAuth()
@UseGuards(AdminRoleGuard)
@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,
);
}
}
} }

View File

@ -0,0 +1,2 @@
export * from './user-device-permission.add.dto';
export * from './user-device-permission.edit.dto';

View File

@ -1,28 +1,29 @@
import { PermissionType } from '@app/common/constants/permission-type.enum';
import { ApiProperty } from '@nestjs/swagger'; import { ApiProperty } from '@nestjs/swagger';
import { IsNotEmpty, IsString } from 'class-validator'; import { IsEnum, IsNotEmpty, IsString } from 'class-validator';
export class UserDevicePermissionAddDto { export class UserDevicePermissionAddDto {
@ApiProperty({ @ApiProperty({
description: 'user id', description: 'user uuid',
required: true, required: true,
}) })
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
userId: string; userUuid: string;
@ApiProperty({ @ApiProperty({
description: 'permission type id', description: 'permission type',
enum: PermissionType,
required: true, required: true,
}) })
@IsString() @IsEnum(PermissionType)
@IsNotEmpty() permissionType: PermissionType;
permissionTypeId: string;
@ApiProperty({ @ApiProperty({
description: 'device id', description: 'device uuid',
required: true, required: true,
}) })
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
deviceId: string; deviceUuid: string;
} }

View File

@ -1,7 +1,13 @@
import { OmitType } from '@nestjs/swagger'; import { PermissionType } from '@app/common/constants/permission-type.enum';
import { UserDevicePermissionAddDto } from './user-device-permission.add.dto'; import { ApiProperty } from '@nestjs/swagger';
import { IsEnum } from 'class-validator';
export class UserDevicePermissionEditDto extends OmitType( export class UserDevicePermissionEditDto {
UserDevicePermissionAddDto, @ApiProperty({
['userId'], description: 'permission type',
) {} enum: PermissionType,
required: true,
})
@IsEnum(PermissionType)
permissionType: PermissionType;
}

View File

@ -0,0 +1 @@
export * from './user-device-permission.service';

View File

@ -1,36 +1,107 @@
import { DeviceUserTypeRepository } from '@app/common/modules/device/repositories'; import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { Injectable } from '@nestjs/common';
import { UserDevicePermissionAddDto } from '../dtos/user-device-permission.add.dto'; import { UserDevicePermissionAddDto } from '../dtos/user-device-permission.add.dto';
import { UserDevicePermissionEditDto } from '../dtos/user-device-permission.edit.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() @Injectable()
export class UserDevicePermissionService { export class UserDevicePermissionService {
constructor( constructor(
private readonly deviceUserTypeRepository: DeviceUserTypeRepository, private readonly deviceUserPermissionRepository: DeviceUserPermissionRepository,
private readonly permissionTypeRepository: PermissionTypeRepository,
) {} ) {}
async addUserPermission(userDevicePermissionDto: UserDevicePermissionAddDto) { async addUserPermission(userDevicePermissionDto: UserDevicePermissionAddDto) {
return await this.deviceUserTypeRepository.save({ try {
userUuid: userDevicePermissionDto.userId, const permissionType = await this.getPermissionType(
deviceUuid: userDevicePermissionDto.deviceId, userDevicePermissionDto.permissionType,
permissionTypeUuid: userDevicePermissionDto.permissionTypeId, );
}); 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( async editUserPermission(
userId: string, devicePermissionUuid: string,
userDevicePermissionEditDto: UserDevicePermissionEditDto, userDevicePermissionEditDto: UserDevicePermissionEditDto,
) { ) {
return await this.deviceUserTypeRepository.update( try {
{ userUuid: userId }, const permissionType = await this.getPermissionType(
{ userDevicePermissionEditDto.permissionType,
deviceUuid: userDevicePermissionEditDto.deviceId, );
permissionTypeUuid: userDevicePermissionEditDto.permissionTypeId, 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() { async fetchUserPermission(deviceUuid: string) {
return await this.deviceUserTypeRepository.find(); 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,
);
}
} }
} }

View File

@ -1,18 +1,18 @@
import { DeviceRepositoryModule } from '@app/common/modules/device'; import { DeviceRepositoryModule } from '@app/common/modules/device';
import { import { DeviceRepository } from '@app/common/modules/device/repositories';
DeviceRepository,
DeviceUserTypeRepository,
} from '@app/common/modules/device/repositories';
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config'; import { ConfigModule } from '@nestjs/config';
import { UserDevicePermissionService } from './services/user-device-permission.service'; import { UserDevicePermissionService } from './services/user-device-permission.service';
import { UserDevicePermissionController } from './controllers/user-device-permission.controller'; 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({ @Module({
imports: [ConfigModule, DeviceRepositoryModule], imports: [ConfigModule, DeviceRepositoryModule],
controllers: [UserDevicePermissionController], controllers: [UserDevicePermissionController],
providers: [ providers: [
DeviceUserTypeRepository, DeviceUserPermissionRepository,
PermissionTypeRepository,
DeviceRepository, DeviceRepository,
UserDevicePermissionService, UserDevicePermissionService,
], ],

View File

@ -2,7 +2,7 @@ import { Controller, Get, Query, UseGuards } from '@nestjs/common';
import { UserService } from '../services/user.service'; import { UserService } from '../services/user.service';
import { UserListDto } from '../dtos/user.list.dto'; import { UserListDto } from '../dtos/user.list.dto';
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
import { JwtAuthGuard } from '../../../libs/common/src/guards/jwt.auth.guard'; import { AdminRoleGuard } from 'src/guards/admin.role.guard';
@ApiTags('User Module') @ApiTags('User Module')
@Controller({ @Controller({
@ -13,7 +13,7 @@ export class UserController {
constructor(private readonly userService: UserService) {} constructor(private readonly userService: UserService) {}
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(JwtAuthGuard) @UseGuards(AdminRoleGuard)
@Get('list') @Get('list')
async userList(@Query() userListDto: UserListDto) { async userList(@Query() userListDto: UserListDto) {
try { try {